/* * Copyright (c) 2012 Marc Alexander Lehmann * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- * CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- * ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * Alternatively, the contents of this file may be used under the terms of * the GNU General Public License ("GPL") version 2 or any later version, * in which case the provisions of the GPL are applicable instead of * the above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use your * version of this file under the BSD license, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete the * provisions above, a recipient may use your version of this file under * either the BSD or the GPL. */ #include "urlib.h" #include #include #include #include #ifdef _WIN32 #include //#include #include #include #include static DWORD dword; #define u_handle HANDLE #define u_invalid_handle 0 #define u_valid(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 | FILE_SHARE_DELETE, 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_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) #define u_fsync(handle) FlushFileBuffers (handle) #define u_sync() #define u_lockfile(path) CreateFile (path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL) #define u_cloexec(handle) #else #define _GNU_SOURCE 1 #define _BSD_SOURCE 1 // the above increases our chances of getting MAP_ANONYMOUS #include #include #include #include #include #if defined (MAP_ANON) && !defined (MAP_ANONYMOUS) #define MAP_ANONYMOUS MAP_ANON #endif #ifdef PATH_MAX #define MAX_PATH (PATH_MAX < 4096 ? 4096 : PATH_MAX) #else #define MAX_PATH 4096 #endif #define u_handle int #define u_invalid_handle -1 #define u_valid(fd) ((fd) >= 0) #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) #define u_creat(path,exec) open (path, O_WRONLY | O_CREAT | O_TRUNC, (exec) ? 0777 : 0666) #define u_close(handle) close (handle) #define u_append(path,add) strcat (strcat (path, "/"), add) #define u_write(handle,data,len) write (handle, data, len) // on a mostly idle system, a sync at the end is certainly faster, hope for the best #define u_fsync(handle) #define u_sync() sync () #define u_lockfile(path) open (path, O_RDWR | O_CREAT, 0666) #define u_cloexec(handle) fcntl (handle, F_SETFD, FD_CLOEXEC) #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 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 void u_fatal (const char *msg) { #ifdef _WIN32 MessageBox (0, msg, URLADER, 0); #else write (2, URLADER ": ", sizeof (URLADER ": ") - 1); write (2, msg, strlen (msg)); write (2, "\n", 1); #endif _exit (1); } static void * u_malloc (unsigned int size) { void *addr; if (!size) return 0; #ifdef _WIN32 { HANDLE handle = CreateFileMapping (0, 0, PAGE_READWRITE, 0, size, NULL); addr = 0; if (handle) { addr = MapViewOfFile (handle, FILE_MAP_WRITE, 0, 0, size); CloseHandle (handle); } } #elif defined (MAP_ANONYMOUS) addr = mmap (0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); if (addr == (void *)-1) addr = 0; #else addr = malloc (size); #endif if (!addr) u_fatal ("memory allocation failure, aborting."); return addr; } static void u_free (void *addr, unsigned int size) { if (!addr) return; #ifdef _WIN32 UnmapViewOfFile (addr); #elif defined (MAP_ANONYMOUS) munmap (addr, size); #else free (addr); #endif } static void * u_realloc (void *addr, unsigned int old_size, unsigned int new_size) { void *addr2 = u_malloc (new_size); memcpy (addr2, addr, (new_size < old_size ? new_size : old_size)); u_free (addr, old_size); return addr2; } static void * u_mmap (u_handle h, unsigned int size) { void *addr; #ifdef _WIN32 HANDLE handle = CreateFileMapping (h, 0, PAGE_READONLY, 0, size, NULL); if (!handle) return 0; addr = MapViewOfFile (handle, FILE_MAP_READ, 0, 0, size); CloseHandle (handle); #else addr = mmap (0, size, PROT_READ, MAP_SHARED, h, 0); if (addr == (void *)-1) addr = 0; #endif return addr; } static void u_munmap (void *addr, unsigned int len) { #ifdef _WIN32 UnmapViewOfFile (addr); #else munmap (addr, len); #endif } ///////////////////////////////////////////////////////////////////////////// typedef struct { char *addr; unsigned int used; unsigned int size; } u_dynbuf; static void * u_dynbuf_append (u_dynbuf *dynbuf, void *data, unsigned int len) { char *dest; if ((dynbuf->used += len) > dynbuf->size) { unsigned int new_size = dynbuf->size ? dynbuf->size * 2 : 4096; dynbuf->addr = u_realloc (dynbuf->addr, dynbuf->size, new_size); dynbuf->size = new_size; } dest = dynbuf->addr + dynbuf->used - len; if (data) memcpy (dest, data, len); return dest; } ///////////////////////////////////////////////////////////////////////////// static void u_set_datadir (void) { #ifdef _WIN32 if (SHGetFolderPath (0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, datadir) != S_OK) u_fatal ("unable to find application data directory"); u_mkdir (datadir); u_append (datadir, URLADER); #else char *home = getenv ("HOME"); if (!home) { struct passwd *pw; if ((pw = getpwuid (getuid ()))) home = pw->pw_dir; else home = "/tmp"; } u_mkdir (home); //strcat (strcat (strcpy (datadir, home), "/."), URLADER); sprintf (datadir, "%s/.%s", home, URLADER); #endif u_setenv ("URLADER_DATADIR", datadir); } static void u_set_exe_info (void) { strcpy (exe_dir, datadir); u_append (exe_dir, exe_id); u_mkdir (exe_dir); strcpy (execdir, exe_dir); u_append (execdir, "i-"); strcat (execdir, exe_ver); 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); } ///////////////////////////////////////////////////////////////////////////// static u_handle u_lock (const char *path, int excl, int dowait) { u_handle h; h = u_lockfile (path); if (!u_valid (h)) return h; u_cloexec (h); for (;;) { int success; // acquire the lock #ifdef _WIN32 OVERLAPPED ov = { 0 }; success = LockFileEx (h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | (dowait ? 0 : LOCKFILE_FAIL_IMMEDIATELY), 0, 1, 0, &ov); #else struct flock lck = { 0 }; lck.l_type = excl ? F_WRLCK : F_RDLCK; lck.l_whence = SEEK_SET; lck.l_len = 1; success = !fcntl (h, dowait ? F_SETLKW : F_SETLK, &lck); #endif if (!success) break; // we have the lock, now verify that the lockfile still exists #ifdef _WIN32 // apparently, we have to open the file to get its info :( { BY_HANDLE_FILE_INFORMATION s1, s2; u_handle h2 = u_lockfile (path); if (!u_valid (h)) break; success = GetFileInformationByHandle (h, &s1) && GetFileInformationByHandle (h2, &s2); u_close (h2); if (!success) break; success = s1.dwVolumeSerialNumber == s2.dwVolumeSerialNumber && s1.nFileIndexHigh == s2.nFileIndexHigh && s1.nFileIndexLow == s2.nFileIndexLow; } #else struct stat s1, s2; if (fstat (h, &s1) || stat (path, &s2)) break; success = s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino; #endif if (success) return h; // lock successfully acquired // files differ, close and retry - should be very rare u_close (h); } // failure u_close (h); return u_invalid_handle; }