#ifndef URLADER # define URLADER "urlader" #endif #define URLADER_VERSION "1.0" #include #include #include #include #include #include #include #include "lzf_d.c" #define TAIL_MAGIC "SCHMORPPACK0" #ifdef _WIN32 #include //#include #include #include #include static DWORD dword; #define u_handle HANDLE #define u_setenv(name,value) SetEnvironmentVariable (name, value) #define u_mkdir(path) !CreateDirectory (path, NULL) #define u_chdir(path) !SetCurrentDirectory (path) #define u_rename(fr,to) !MoveFile (fr, to) #define u_open(path) CreateFile (path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL) #define u_creat(path,exec) CreateFile (path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL) #define u_close(handle) CloseHandle (handle) #define u_append(path,add) PathAppend (path, add) #define u_write(handle,data,len) (WriteFile (handle, data, len, &dword, 0) ? dword : -1) #else #include #include #include #ifdef PATH_MAX #define MAX_PATH (PATH_MAX < 4096 ? 4096 : PATH_MAX) #else #define MAX_PATH 4096 #endif #define u_handle int #define u_setenv(name,value) setenv (name, value, 1) #define u_mkdir(path) mkdir (path, 0777) #define u_chdir(path) chdir (path) #define u_rename(fr,to) rename (fr, to) #define u_open(path) open (path, O_RDONLY) + 1 #define u_creat(path,exec) open (path, O_WRONLY | O_CREAT | O_TRUNC, (exec) ? 0777 : 0666) + 1 #define u_close(handle) close (handle - 1) #define u_append(path,add) strcat (strcat (path, "/"), add) #define u_write(handle,data,len) write ((handle) - 1, data, len) #endif #define u_16(ptr) (((ptr)[0] << 8) | (ptr)[1]) #define u_32(ptr) (((ptr)[0] << 24) | ((ptr)[1] << 16) | ((ptr)[2] << 8) | (ptr)[3]) static u_handle pack_handle; static char tmppath[MAX_PATH]; static char currdir[MAX_PATH]; static char datadir[MAX_PATH]; // %AppData%/urlader static char exe_dir[MAX_PATH]; // %AppData%/urlader/EXE_ID static char execdir[MAX_PATH]; // %AppData%/urlader/EXE_ID/EXE_VER static char exe_id[MAX_PATH]; static char exe_ver[MAX_PATH]; static int exe_argc; static const char *exe_argv[32]; static void fatal (const char *msg) { #ifdef _WIN32 MessageBox (0, msg, URLADER, 0); #else fprintf (stderr, "%s: %s\n", URLADER, msg); #endif _exit (1); } static void tmpdir (const char *dir) { static int cnt; for (;;) { // #ifdef _WIN32 // sprintf (tmppath, "%s/%x_%x.tmp", dir, (unsigned int)GetCurrentProcessId (), ++cnt); // #else sprintf (tmppath, "%s/t-%x_%x.tmp", dir, (unsigned int)getpid () , ++cnt); // #endif if (!u_mkdir (tmppath)) return; } } static void systemv (const char *const argv[]) { #ifdef _WIN32 _spawnv (P_WAIT, argv [0], argv); #else pid_t pid = fork (); if (pid < 0) fatal ("fork failure"); if (!pid) { execv (argv [0], (void *)argv); _exit (124); } int status; waitpid (pid, &status, 0); #endif } static void deltree (const char *path) { #ifdef _WIN32 char buf[MAX_PATH * 2 + 64]; const char *argv[] = { getenv ("COMSPEC"), "/c", "rd", "/s", "/q", buf, 0 }; sprintf (buf, "\"%s\"", path); #else const char *argv[] = { "/bin/rm", "-rf", path, 0 }; #endif systemv (argv); } static void execute (void) { exe_argv [exe_argc] = 0; systemv (exe_argv); } enum { T_NULL, // 5 T_META, // 1 : exe_id, exe_ver T_ARG, // 2 : arg T_ENV, // 3 : name, value T_DIR, // 4+: path T_FILE, // 4+: path, data T_NUM }; enum { F_EXEC = 1, F_LZF = 2, F_NULL = 0 }; struct hdr { unsigned char type; unsigned char flags; unsigned char namelen[2]; unsigned char datalen[4]; }; struct tail { unsigned char max_filesize[4]; unsigned char size[4]; char magic[12]; }; static char *pack_base, *pack_end; static struct hdr *pack_cur; #define PACK_NAME ((char *)(pack_cur + 1)) #define PACK_DATA (PACK_NAME + u_16 (pack_cur->namelen) + 1) #define PACK_VALID pack_cur->type static void pack_next (void) { unsigned int d = u_32 (pack_cur->datalen); pack_cur = (struct hdr *)(PACK_DATA + d); } static void pack_unmap (void) { if (!pack_base) return; #ifdef _WIN32 UnmapViewOfFile (pack_base); #else munmap (pack_base, pack_end - pack_base); #endif } static int pack_map (void) { #ifdef _WIN32 BY_HANDLE_FILE_INFORMATION fi; if (!GetFileInformationByHandle (pack_handle, &fi)) return 0; if (fi.nFileSizeHigh) // too big return 0; HANDLE handle = CreateFileMapping (pack_handle, 0, PAGE_READONLY, 0, fi.nFileSizeLow, NULL); if (!handle) return 0; pack_unmap (); pack_base = MapViewOfFile (handle, FILE_MAP_READ, 0, 0, fi.nFileSizeLow); CloseHandle (handle); pack_end = pack_base + fi.nFileSizeLow; #else off_t len = lseek (pack_handle - 1, 0, SEEK_END); pack_unmap (); pack_base = mmap (0, len, PROT_READ, MAP_SHARED, pack_handle - 1, 0); if (pack_base == (void *)-1) pack_base = 0; pack_end = pack_base + len; #endif if (!pack_base) return 0; struct tail *tail; tail = (void *)(pack_end - sizeof (*tail)); if (memcmp (tail->magic, TAIL_MAGIC, sizeof (TAIL_MAGIC) - 1)) return 0; pack_cur = (struct hdr *)(pack_end - u_32 (tail->size)); return 1; } static void exe_info (void) { if (!pack_map ()) fatal ("unable to locate packfile in executable - executable corrupted?"); if (pack_cur->type != T_META) fatal ("unable to locate executable metadata - executable corrupted?"); strcpy (exe_id, PACK_NAME); } static void load (void) { u_mkdir (datadir); strcpy (exe_dir, datadir); u_append (exe_dir, exe_id); u_mkdir (exe_dir); if (u_chdir (exe_dir)) fatal ("unable to change to application data directory"); u_handle h = u_open ("override"); if (h) { u_handle oh = pack_handle; pack_handle = h; if (!pack_map ()) { pack_handle = oh; oh = h; } u_close (oh); } if (pack_cur->type != T_META) fatal ("unable to locate override metadata"); strcpy (exe_ver, PACK_DATA); pack_next (); while (pack_cur->type == T_ARG) { exe_argv [exe_argc++] = strdup (PACK_NAME); pack_next (); } while (pack_cur->type == T_ENV) { u_setenv (PACK_NAME, PACK_DATA); pack_next (); } strcpy (execdir, exe_dir); u_append (execdir, "i-"); strcat (execdir, exe_ver); if (access (execdir, F_OK)) { // does not exist yet, so unpack and move tmpdir (exe_dir); if (u_chdir (tmppath)) fatal ("unable to change to new instance directory"); for (;;) { switch (pack_cur->type) { case T_DIR: u_mkdir (PACK_NAME); break; case T_FILE: { u_handle h = u_creat (PACK_NAME, pack_cur->flags & F_EXEC); unsigned int len = u_32 (pack_cur->datalen); if (!h) fatal ("unable to unpack file from packfile - disk full?"); if (u_write (h, PACK_DATA, len) != len) fatal ("unable to unpack file from packfile - disk full?"); u_close (h); } break; case T_NULL: goto done; } pack_next (); } done: if (u_chdir (datadir)) fatal ("unable to change to data directory"); if (u_rename (tmppath, execdir)) deltree (tmppath); // if move fails, delete new, assume other process created it independently if (u_chdir (execdir)) fatal ("unable to change to application instance directory"); } pack_unmap (); u_close (pack_handle); u_setenv ("URLADER_VERSION", URLADER_VERSION); u_setenv ("URLADER_DATADIR", datadir); u_setenv ("URLADER_EXECDIR", execdir); u_setenv ("URLADER_EXE_ID" , exe_id); u_setenv ("URLADER_EXE_DIR", exe_dir); u_setenv ("URLADER_EXE_VER", exe_ver); // yes, this is overkill u_setenv ("SHLIB_PATH" , execdir); // hpux u_setenv ("LIBPATH" , execdir); // aix u_setenv ("LD_LIBRARY_PATH" , execdir); // most elf systems u_setenv ("LD_LIBRARY_PATH_32", execdir); // solaris u_setenv ("LD_LIBRARY_PATH_64", execdir); // solaris u_setenv ("LD_LIBRARYN32_PATH", execdir); // irix u_setenv ("LD_LIBRARY64_PATH" , execdir); // irix u_setenv ("DYLD_LIBRARY_PATH" , execdir); // os sucks from apple } #ifdef _WIN32 int APIENTRY WinMain (HINSTANCE hI, HINSTANCE hP, LPSTR argv, int command_show) { if (!GetModuleFileName (hI, tmppath, sizeof (tmppath))) fatal ("unable to find executable pack"); if (!(pack_handle = u_open (tmppath))) fatal ("unable to open executable pack"); exe_info (); if (!GetCurrentDirectory (sizeof (currdir), currdir)) strcpy (currdir, "."); if (SHGetFolderPath (0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, datadir) != S_OK) fatal ("unable to find application data directory"); u_mkdir (datadir); u_append (datadir, URLADER); load (); execute (); return 0; } #else int main (int argc, char *argv[]) { char *home = getenv ("HOME"); // we assume fd 0 and 2 are "normal" if (!(pack_handle = u_open (argv [0]))) fatal ("unable to open executable pack"); exe_info (); if (!home) { struct passwd *pw; if ((pw = getpwuid (getuid ()))) home = pw->pw_dir; else home = "/tmp"; } if (!getcwd (currdir, sizeof (currdir))) strcpy (currdir, "."); u_mkdir (home); sprintf (datadir, "%s/.%s", home, URLADER); u_mkdir (datadir); if (gethostname (tmppath, sizeof (tmppath))) strcpy (tmppath, "default"); u_append (datadir, tmppath); load (); execute (); return 0; } #endif