FlatImage
A configurable Linux containerization system
Loading...
Searching...
No Matches
relocate.hpp
Go to the documentation of this file.
1
8
9#include <expected>
10#include <string>
11#include <system_error>
12#include <filesystem>
13#include <print>
14
15#include "../macro.hpp"
16#include "../lib/env.hpp"
17#include "../lib/elf.hpp"
18#include "../db/db.hpp"
19#include "../std/expected.hpp"
20#include "../config.hpp"
21
30namespace ns_relocate
31{
32
33namespace
34{
35
36namespace fs = std::filesystem;
37
38constexpr std::array<const char*,403> const arr_busybox_applet
39{
40 "[","[[","acpid","add-shell","addgroup","adduser","adjtimex","arch","arp","arping","ascii","ash","awk","base32","base64",
41 "basename","bc","beep","blkdiscard","blkid","blockdev","bootchartd","brctl","bunzip2","bzcat","bzip2","cal","cat","chat",
42 "chattr","chgrp","chmod","chown","chpasswd","chpst","chroot","chrt","chvt","cksum","clear","cmp","comm","conspy","cp","cpio",
43 "crc32","crond","crontab","cryptpw","cttyhack","cut","date","dc","dd","deallocvt","delgroup","deluser","depmod","devmem",
44 "df","dhcprelay","diff","dirname","dmesg","dnsd","dnsdomainname","dos2unix","dpkg","dpkg-deb","du","dumpkmap",
45 "dumpleases","echo","ed","egrep","eject","env","envdir","envuidgid","ether-wake","expand","expr","factor","fakeidentd",
46 "fallocate","false","fatattr","fbset","fbsplash","fdflush","fdformat","fdisk","fgconsole","fgrep","find","findfs",
47 "flock","fold","free","freeramdisk","fsck","fsck.minix","fsfreeze","fstrim","fsync","ftpd","ftpget","ftpput","fuser",
48 "getfattr","getopt","getty","grep","groups","gunzip","gzip","halt","hd","hdparm","head","hexdump","hexedit","hostid",
49 "hostname","httpd","hush","hwclock","i2cdetect","i2cdump","i2cget","i2cset","i2ctransfer","id","ifconfig","ifdown",
50 "ifenslave","ifplugd","ifup","inetd","init","insmod","install","ionice","iostat","ip","ipaddr","ipcalc","ipcrm","ipcs",
51 "iplink","ipneigh","iproute","iprule","iptunnel","kbd_mode","kill","killall","killall5","klogd","last","less","link",
52 "linux32","linux64","linuxrc","ln","loadfont","loadkmap","logger","login","logname","logread","losetup","lpd","lpq",
53 "lpr","ls","lsattr","lsmod","lsof","lspci","lsscsi","lsusb","lzcat","lzma","lzop","makedevs","makemime","man","md5sum",
54 "mdev","mesg","microcom","mim","mkdir","mkdosfs","mke2fs","mkfifo","mkfs.ext2","mkfs.minix","mkfs.vfat","mknod",
55 "mkpasswd","mkswap","mktemp","modinfo","modprobe","more","mount","mountpoint","mpstat","mt","mv","nameif","nanddump",
56 "nandwrite","nbd-client","nc","netstat","nice","nl","nmeter","nohup","nologin","nproc","nsenter","nslookup","ntpd","od",
57 "openvt","partprobe","passwd","paste","patch","pgrep","pidof","ping","ping6","pipe_progress","pivot_root","pkill",
58 "pmap","popmaildir","poweroff","powertop","printenv","printf","ps","pscan","pstree","pwd","pwdx","raidautorun","rdate",
59 "rdev","readahead","readlink","readprofile","realpath","reboot","reformime","remove-shell","renice","reset",
60 "resize","resume","rev","rm","rmdir","rmmod","route","rpm","rpm2cpio","rtcwake","run-init","run-parts","runlevel",
61 "runsv","runsvdir","rx","script","scriptreplay","sed","seedrng","sendmail","seq","setarch","setconsole","setfattr",
62 "setfont","setkeycodes","setlogcons","setpriv","setserial","setsid","setuidgid","sh","sha1sum","sha256sum",
63 "sha3sum","sha512sum","showkey","shred","shuf","slattach","sleep","smemcap","softlimit","sort","split","ssl_client",
64 "start-stop-daemon","stat","strings","stty","su","sulogin","sum","sv","svc","svlogd","svok","swapoff","swapon",
65 "switch_root","sync","sysctl","syslogd","tac","tail","tar","taskset","tc","tcpsvd","tee","telnet","telnetd","test","tftp",
66 "tftpd","time","timeout","top","touch","tr","traceroute","traceroute6","tree","true","truncate","ts","tsort","tty",
67 "ttysize","tunctl","ubiattach","ubidetach","ubimkvol","ubirename","ubirmvol","ubirsvol","ubiupdatevol","udhcpc",
68 "udhcpc6","udhcpd","udpsvd","uevent","umount","uname","unexpand","uniq","unix2dos","unlink","unlzma","unshare","unxz",
69 "unzip","uptime","users","usleep","uudecode","uuencode","vconfig","vi","vlock","volname","w","wall","watch","watchdog",
70 "wc","wget","which","who","whoami","whois","xargs","xxd","xz","xzcat","yes","zcat","zcip",
71};
72
85[[nodiscard]] inline Value<void> relocate_impl(char** argv
86 , uint32_t offset
87 , fs::path const& path_file_self)
88{
89 // Save the original path before relocation
90 ns_env::set("FIM_BIN_SELF", path_file_self, ns_env::Replace::Y);
91 // This part of the code is executed to write the runner,
92 // rightafter the code is replaced by the runner.
93 // This is done because the current executable cannot mount itself.
94 // Configure directory paths
95 ns_config::Path const path = Pop(ns_config::Path::create());
96 auto const dir = path.dir;
97 auto const bin = path.bin;
98 // Create directories
99 Try(fs::create_directories(dir.global));
100 Try(fs::create_directories(dir.app));
101 Try(fs::create_directories(dir.app_bin));
102 Try(fs::create_directories(dir.app_sbin));
103 Try(fs::create_directories(dir.instance));
104 // Starting offsets
105 uint64_t offset_beg = 0;
106 uint64_t offset_end = Pop(ns_elf::skip_elf_header(bin.self.c_str()));
117 auto f_write_from_header = [&](fs::path path_file, uint64_t offset_end)
118 -> Value<std::pair<uint64_t,uint64_t>>
119 {
120 // Update offsets
121 offset_beg = offset_end;
122 offset_end = Pop(ns_elf::skip_elf_header(bin.self.c_str(),offset_beg)) + offset_beg;
123 // Write binary only if it doesnt already exist
124 if ( ! Try(fs::exists(path_file)) )
125 {
126 Pop(ns_elf::copy_binary(bin.self.string()
127 , path_file
128 , {offset_beg
129 , offset_end})
130 );
131 }
132 // Set permissions (with error_code - doesn't throw)
133 Try(fs::permissions(path_file.c_str(), fs::perms::owner_all | fs::perms::group_all));
134 // Return new values for offsets
135 return std::make_pair(offset_beg, offset_end);
136 };
137
149 auto f_write_from_offset = [&](std::ifstream& file_binary, fs::path path_file, uint64_t offset_end)
150 -> Value<std::pair<uint64_t,uint64_t>>
151 {
152 std::error_code ec;
153 uint64_t offset_beg = offset_end;
154 logger("D::Writting binary file '{}'", path_file);
155 // Set file position
156 file_binary.seekg(offset_beg);
157 // Read size bytes (FATAL if fails)
158 uint64_t size;
159 return_if(not file_binary.read(reinterpret_cast<char*>(&size), sizeof(size)), Error("E::Could not read binary size"));
160 // Open output file and write
161 if (fs::exists(path_file, ec))
162 {
163 file_binary.seekg(offset_beg + size + sizeof(size));
164 } // if
165 else
166 {
167 // Read binary
168 std::vector<char> buffer(size);
169 return_if(not file_binary.read(buffer.data(), size), Error("E::Could not read binary"));
170 // Open output binary file
171 std::ofstream of{path_file, std::ios::out | std::ios::binary};
172 return_if(not of.is_open(), Error("E::Could not open output file '{}'", path_file));
173 // Write binary
174 return_if(not of.write(buffer.data(), size), Error("E::Could not write binary file '{}'", path_file));
175 // Set permissions (with error_code - doesn't throw)
176 fs::permissions(path_file, fs::perms::owner_all | fs::perms::group_all, ec);
177 log_if(ec, "E::Error on setting permissions of file '{}': {}", path_file.string(), ec.message());
178 } // if
179 // Return new values for offsets
180 return std::make_pair(offset_beg, file_binary.tellg());
181 };
182
183 // Write binaries
184 auto start = std::chrono::high_resolution_clock::now();
185 std::ifstream file_binary{bin.self, std::ios::in | std::ios::binary};
186 return_if(not file_binary.is_open(), Error("E::Could not open flatimage binary file"));
187 constexpr static char const str_raw_json[] =
188 {
189 #embed FIM_FILE_TOOLS
190 };
191 std::tie(offset_beg, offset_end) = Pop(f_write_from_header(dir.app_bin / "fim_boot" , 0));
192 // TODO: Make this compile-time with C++26 reflection features
193 for(auto&& tool : Pop(Pop(ns_db::from_string(str_raw_json)).template value<std::vector<std::string>>()))
194 {
195 std::tie(offset_beg, offset_end) = Pop(f_write_from_offset(file_binary, dir.app_bin / tool, offset_end));
196 }
197 file_binary.close();
198 auto f_symlink = [](auto&& points_to, auto&& saved_at)
199 {
200 if(fs::exists(saved_at)) { fs::remove(saved_at); }
201 fs::create_symlink(points_to, saved_at);
202 };
203 // Create symlinks (with error_code - doesn't throw)
204 fs::path path_file_dwarfs_aio = dir.app_bin / "dwarfs_aio";
205 Try(f_symlink(path_file_dwarfs_aio, dir.app_bin / "dwarfs"));
206 Try(f_symlink(path_file_dwarfs_aio, dir.app_bin / "mkdwarfs"));
207 auto end = std::chrono::high_resolution_clock::now();
208
209 // Create busybox symlinks, allow (symlinks exists) errors
210 for(auto const& busybox_applet : arr_busybox_applet)
211 {
212 Try(f_symlink(dir.app_bin / "busybox", dir.app_sbin / busybox_applet));
213 } // for
214
215 // Filesystem starts here
216 ns_env::set("FIM_OFFSET", std::to_string(offset_end).c_str(), ns_env::Replace::Y);
217 return_if(offset_end != offset, Error("E::Broken image actual offset({}) != offset({})", offset_end, offset));
218 logger("D::FIM_OFFSET: {}", offset_end);
219
220 // Option to show offset and exit (to manually mount the fs with fuse2fs)
221 if( getenv("FIM_MAIN_OFFSET") ){ std::println("{}", offset_end); exit(0); }
222
223 // Print copy duration
224 if ( getenv("FIM_DEBUG") != nullptr )
225 {
226 auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
227 logger("D::Copy binaries finished in '{}' ms", elapsed.count());
228 } // if
229
230 // Launch Runner
231 int code = execve(std::format("{}/fim_boot", dir.app_bin.string()).c_str(), argv, environ);
232 return Error("E::Could not perform 'evecve({})': {}", code, strerror(errno));
233}
234
235} // namespace
236
244[[nodiscard]] inline Value<void> relocate(char** argv, int32_t offset)
245{
246 // Get path to self
247 fs::path path_file_self = Try(fs::read_symlink("/proc/self/exe"));
248 // If it is outside /tmp, move the binary
249 if (Try(fs::file_size(path_file_self)) != Pop(ns_elf::skip_elf_header(path_file_self)))
250 {
251 Pop(relocate_impl(argv, offset, path_file_self), "E::Could not relocate binary");
252 }
253 return {};
254}
255
256} // namespace ns_relocate
Defines all fundamental FlatImage paths.
Definition config.hpp:143
static Value< Path > create()
Factory method to create Path configuration.
Definition config.hpp:257
FlatImage configuration object.
A database that interfaces with nlohmann json.
A library for operations on ELF files.
Enhanced error handling framework built on std::expected.
A library for manipulating environment variables.
#define logger(fmt,...)
Compile-time log level dispatch macro with automatic location capture.
Definition log.hpp:682
Simplified macros for common control flow patterns with optional logging.
Value< Db > from_string(S &&s)
Parses a JSON string and creates a Db object.
Definition db.hpp:496
Value< uint64_t > skip_elf_header(fs::path const &path_file_elf, uint64_t offset=0)
Skips the elf header starting from 'offset' and returns the offset to the first byte afterwards.
Definition elf.hpp:85
Value< void > copy_binary(fs::path const &path_file_input, fs::path const &path_file_output, std::pair< uint64_t, uint64_t > section)
Copies the binary data between [offset.first, offset.second] from path_file_input to path_file_output...
Definition elf.hpp:52
void set(T &&name, U &&value, Replace replace)
Sets an environment variable.
Definition env.hpp:52
Value< void > relocate_impl(char **argv, uint32_t offset, fs::path const &path_file_self)
Relocate the binary (by copying it) from the image.
Definition relocate.hpp:85
Binary relocation and extraction utilities.
Value< void > relocate(char **argv, int32_t offset)
Calls the implementation of relocate.
Definition relocate.hpp:244
Enhanced expected type with integrated logging capabilities.
Definition expected.hpp:44