/*
* Copyright (c) 2012 Marc Alexander Lehmann <schmorp@schmorp.de>
*
* 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 <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
//#include <winbase.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <wininet.h>
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 <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#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;
}