1 |
/* |
2 |
* Copyright (c) 2012 Marc Alexander Lehmann <schmorp@schmorp.de> |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without modifica- |
5 |
* tion, are permitted provided that the following conditions are met: |
6 |
* |
7 |
* 1. Redistributions of source code must retain the above copyright notice, |
8 |
* this list of conditions and the following disclaimer. |
9 |
* |
10 |
* 2. Redistributions in binary form must reproduce the above copyright |
11 |
* notice, this list of conditions and the following disclaimer in the |
12 |
* documentation and/or other materials provided with the distribution. |
13 |
* |
14 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
15 |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MER- |
16 |
* CHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
17 |
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPE- |
18 |
* CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
19 |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
20 |
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
21 |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTH- |
22 |
* ERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
23 |
* OF THE POSSIBILITY OF SUCH DAMAGE. |
24 |
* |
25 |
* Alternatively, the contents of this file may be used under the terms of |
26 |
* the GNU General Public License ("GPL") version 2 or any later version, |
27 |
* in which case the provisions of the GPL are applicable instead of |
28 |
* the above. If you wish to allow the use of your version of this file |
29 |
* only under the terms of the GPL and not to allow others to use your |
30 |
* version of this file under the BSD license, indicate your decision |
31 |
* by deleting the provisions above and replace them with the notice |
32 |
* and other provisions required by the GPL. If you do not delete the |
33 |
* provisions above, a recipient may use your version of this file under |
34 |
* either the BSD or the GPL. |
35 |
*/ |
36 |
|
37 |
#include "urlib.h" |
38 |
|
39 |
#include <fcntl.h> |
40 |
#include <stdio.h> |
41 |
#include <string.h> |
42 |
#include <stdlib.h> |
43 |
|
44 |
#ifdef _WIN32 |
45 |
|
46 |
#include <windows.h> |
47 |
//#include <winbase.h> |
48 |
#include <shlobj.h> |
49 |
#include <shlwapi.h> |
50 |
#include <wininet.h> |
51 |
|
52 |
static DWORD dword; |
53 |
|
54 |
#define u_handle HANDLE |
55 |
#define u_invalid_handle 0 |
56 |
#define u_valid(handle) (!!handle) |
57 |
|
58 |
#define u_setenv(name,value) SetEnvironmentVariable (name, value) |
59 |
#define u_mkdir(path) !CreateDirectory (path, NULL) |
60 |
#define u_chdir(path) !SetCurrentDirectory (path) |
61 |
#define u_rename(fr,to) !MoveFile (fr, to) |
62 |
#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) |
63 |
#define u_creat(path,exec) CreateFile (path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL) |
64 |
#define u_creat(path,exec) CreateFile (path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL) |
65 |
#define u_close(handle) CloseHandle (handle) |
66 |
#define u_append(path,add) PathAppend (path, add) |
67 |
#define u_write(handle,data,len) (WriteFile (handle, data, len, &dword, 0) ? dword : -1) |
68 |
|
69 |
#define u_fsync(handle) FlushFileBuffers (handle) |
70 |
#define u_sync() |
71 |
|
72 |
#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) |
73 |
#define u_cloexec(handle) |
74 |
|
75 |
#else |
76 |
|
77 |
#define _GNU_SOURCE 1 |
78 |
#define _BSD_SOURCE 1 |
79 |
// the above increases our chances of getting MAP_ANONYMOUS |
80 |
|
81 |
#include <sys/mman.h> |
82 |
#include <sys/types.h> |
83 |
#include <sys/stat.h> |
84 |
#include <unistd.h> |
85 |
#include <pwd.h> |
86 |
|
87 |
#if defined (MAP_ANON) && !defined (MAP_ANONYMOUS) |
88 |
#define MAP_ANONYMOUS MAP_ANON |
89 |
#endif |
90 |
|
91 |
#ifdef PATH_MAX |
92 |
#define MAX_PATH (PATH_MAX < 4096 ? 4096 : PATH_MAX) |
93 |
#else |
94 |
#define MAX_PATH 4096 |
95 |
#endif |
96 |
|
97 |
#define u_handle int |
98 |
#define u_invalid_handle -1 |
99 |
#define u_valid(fd) ((fd) >= 0) |
100 |
|
101 |
#define u_setenv(name,value) setenv (name, value, 1) |
102 |
#define u_mkdir(path) mkdir (path, 0777) |
103 |
#define u_chdir(path) chdir (path) |
104 |
#define u_rename(fr,to) rename (fr, to) |
105 |
#define u_open(path) open (path, O_RDONLY) |
106 |
#define u_creat(path,exec) open (path, O_WRONLY | O_CREAT | O_TRUNC, (exec) ? 0777 : 0666) |
107 |
#define u_close(handle) close (handle) |
108 |
#define u_append(path,add) strcat (strcat (path, "/"), add) |
109 |
#define u_write(handle,data,len) write (handle, data, len) |
110 |
|
111 |
// on a mostly idle system, a sync at the end is certainly faster, hope for the best |
112 |
#define u_fsync(handle) |
113 |
#define u_sync() sync () |
114 |
|
115 |
#define u_lockfile(path) open (path, O_RDWR | O_CREAT, 0666) |
116 |
#define u_cloexec(handle) fcntl (handle, F_SETFD, FD_CLOEXEC) |
117 |
|
118 |
#endif |
119 |
|
120 |
#define u_16(ptr) (((ptr)[0] << 8) | (ptr)[1]) |
121 |
#define u_32(ptr) (((ptr)[0] << 24) | ((ptr)[1] << 16) | ((ptr)[2] << 8) | (ptr)[3]) |
122 |
|
123 |
static char currdir[MAX_PATH]; |
124 |
static char datadir[MAX_PATH]; // %AppData%/urlader |
125 |
static char exe_dir[MAX_PATH]; // %AppData%/urlader/EXE_ID |
126 |
static char execdir[MAX_PATH]; // %AppData%/urlader/EXE_ID/EXE_VER |
127 |
static char exe_id[MAX_PATH]; |
128 |
static char exe_ver[MAX_PATH]; |
129 |
|
130 |
///////////////////////////////////////////////////////////////////////////// |
131 |
|
132 |
static void |
133 |
u_fatal (const char *msg) |
134 |
{ |
135 |
#ifdef _WIN32 |
136 |
MessageBox (0, msg, URLADER, 0); |
137 |
#else |
138 |
write (2, URLADER ": ", sizeof (URLADER ": ") - 1); |
139 |
write (2, msg, strlen (msg)); |
140 |
write (2, "\n", 1); |
141 |
#endif |
142 |
|
143 |
_exit (1); |
144 |
} |
145 |
|
146 |
static void * |
147 |
u_malloc (unsigned int size) |
148 |
{ |
149 |
void *addr; |
150 |
|
151 |
if (!size) |
152 |
return 0; |
153 |
|
154 |
#ifdef _WIN32 |
155 |
{ |
156 |
HANDLE handle = CreateFileMapping (0, 0, PAGE_READWRITE, 0, size, NULL); |
157 |
|
158 |
addr = 0; |
159 |
if (handle) |
160 |
{ |
161 |
addr = MapViewOfFile (handle, FILE_MAP_WRITE, 0, 0, size); |
162 |
CloseHandle (handle); |
163 |
} |
164 |
} |
165 |
#elif defined (MAP_ANONYMOUS) |
166 |
addr = mmap (0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); |
167 |
|
168 |
if (addr == (void *)-1) |
169 |
addr = 0; |
170 |
#else |
171 |
addr = malloc (size); |
172 |
#endif |
173 |
|
174 |
if (!addr) |
175 |
u_fatal ("memory allocation failure, aborting."); |
176 |
|
177 |
return addr; |
178 |
} |
179 |
|
180 |
static void |
181 |
u_free (void *addr, unsigned int size) |
182 |
{ |
183 |
if (!addr) |
184 |
return; |
185 |
|
186 |
#ifdef _WIN32 |
187 |
UnmapViewOfFile (addr); |
188 |
#elif defined (MAP_ANONYMOUS) |
189 |
munmap (addr, size); |
190 |
#else |
191 |
free (addr); |
192 |
#endif |
193 |
} |
194 |
|
195 |
static void * |
196 |
u_realloc (void *addr, unsigned int old_size, unsigned int new_size) |
197 |
{ |
198 |
void *addr2 = u_malloc (new_size); |
199 |
memcpy (addr2, addr, (new_size < old_size ? new_size : old_size)); |
200 |
u_free (addr, old_size); |
201 |
|
202 |
return addr2; |
203 |
} |
204 |
|
205 |
static void * |
206 |
u_mmap (u_handle h, unsigned int size) |
207 |
{ |
208 |
void *addr; |
209 |
|
210 |
#ifdef _WIN32 |
211 |
HANDLE handle = CreateFileMapping (h, 0, PAGE_READONLY, 0, size, NULL); |
212 |
|
213 |
if (!handle) |
214 |
return 0; |
215 |
|
216 |
addr = MapViewOfFile (handle, FILE_MAP_READ, 0, 0, size); |
217 |
|
218 |
CloseHandle (handle); |
219 |
#else |
220 |
addr = mmap (0, size, PROT_READ, MAP_SHARED, h, 0); |
221 |
|
222 |
if (addr == (void *)-1) |
223 |
addr = 0; |
224 |
#endif |
225 |
|
226 |
return addr; |
227 |
} |
228 |
|
229 |
static void |
230 |
u_munmap (void *addr, unsigned int len) |
231 |
{ |
232 |
#ifdef _WIN32 |
233 |
UnmapViewOfFile (addr); |
234 |
#else |
235 |
munmap (addr, len); |
236 |
#endif |
237 |
} |
238 |
|
239 |
///////////////////////////////////////////////////////////////////////////// |
240 |
|
241 |
typedef struct |
242 |
{ |
243 |
char *addr; |
244 |
unsigned int used; |
245 |
unsigned int size; |
246 |
} u_dynbuf; |
247 |
|
248 |
static void * |
249 |
u_dynbuf_append (u_dynbuf *dynbuf, void *data, unsigned int len) |
250 |
{ |
251 |
char *dest; |
252 |
|
253 |
if ((dynbuf->used += len) > dynbuf->size) |
254 |
{ |
255 |
unsigned int new_size = dynbuf->size ? dynbuf->size * 2 : 4096; |
256 |
dynbuf->addr = u_realloc (dynbuf->addr, dynbuf->size, new_size); |
257 |
dynbuf->size = new_size; |
258 |
} |
259 |
|
260 |
dest = dynbuf->addr + dynbuf->used - len; |
261 |
|
262 |
if (data) |
263 |
memcpy (dest, data, len); |
264 |
|
265 |
return dest; |
266 |
} |
267 |
|
268 |
///////////////////////////////////////////////////////////////////////////// |
269 |
|
270 |
static void |
271 |
u_set_datadir (void) |
272 |
{ |
273 |
#ifdef _WIN32 |
274 |
if (SHGetFolderPath (0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, datadir) != S_OK) |
275 |
u_fatal ("unable to find application data directory"); |
276 |
|
277 |
u_mkdir (datadir); |
278 |
u_append (datadir, URLADER); |
279 |
|
280 |
#else |
281 |
char *home = getenv ("HOME"); |
282 |
|
283 |
if (!home) |
284 |
{ |
285 |
struct passwd *pw; |
286 |
|
287 |
if ((pw = getpwuid (getuid ()))) |
288 |
home = pw->pw_dir; |
289 |
else |
290 |
home = "/tmp"; |
291 |
} |
292 |
|
293 |
u_mkdir (home); |
294 |
//strcat (strcat (strcpy (datadir, home), "/."), URLADER); |
295 |
sprintf (datadir, "%s/.%s", home, URLADER); |
296 |
#endif |
297 |
|
298 |
u_setenv ("URLADER_DATADIR", datadir); |
299 |
} |
300 |
|
301 |
static void |
302 |
u_set_exe_info (void) |
303 |
{ |
304 |
strcpy (exe_dir, datadir); |
305 |
u_append (exe_dir, exe_id); |
306 |
u_mkdir (exe_dir); |
307 |
|
308 |
strcpy (execdir, exe_dir); |
309 |
u_append (execdir, "i-"); |
310 |
strcat (execdir, exe_ver); |
311 |
|
312 |
u_setenv ("URLADER_EXECDIR", execdir); |
313 |
u_setenv ("URLADER_EXE_ID" , exe_id); |
314 |
u_setenv ("URLADER_EXE_DIR", exe_dir); |
315 |
u_setenv ("URLADER_EXE_VER", exe_ver); |
316 |
} |
317 |
|
318 |
///////////////////////////////////////////////////////////////////////////// |
319 |
|
320 |
static u_handle |
321 |
u_lock (const char *path, int excl, int dowait) |
322 |
{ |
323 |
u_handle h; |
324 |
|
325 |
h = u_lockfile (path); |
326 |
if (!u_valid (h)) |
327 |
return h; |
328 |
|
329 |
u_cloexec (h); |
330 |
|
331 |
for (;;) |
332 |
{ |
333 |
int success; |
334 |
|
335 |
// acquire the lock |
336 |
#ifdef _WIN32 |
337 |
OVERLAPPED ov = { 0 }; |
338 |
|
339 |
success = LockFileEx (h, |
340 |
(excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) |
341 |
| (dowait ? 0 : LOCKFILE_FAIL_IMMEDIATELY), |
342 |
0, |
343 |
1, 0, |
344 |
&ov); |
345 |
#else |
346 |
struct flock lck = { 0 }; |
347 |
|
348 |
lck.l_type = excl ? F_WRLCK : F_RDLCK; |
349 |
lck.l_whence = SEEK_SET; |
350 |
lck.l_len = 1; |
351 |
|
352 |
success = !fcntl (h, dowait ? F_SETLKW : F_SETLK, &lck); |
353 |
#endif |
354 |
|
355 |
if (!success) |
356 |
break; |
357 |
|
358 |
// we have the lock, now verify that the lockfile still exists |
359 |
|
360 |
#ifdef _WIN32 |
361 |
// apparently, we have to open the file to get its info :( |
362 |
{ |
363 |
BY_HANDLE_FILE_INFORMATION s1, s2; |
364 |
u_handle h2 = u_lockfile (path); |
365 |
if (!u_valid (h)) |
366 |
break; |
367 |
|
368 |
success = GetFileInformationByHandle (h, &s1) |
369 |
&& GetFileInformationByHandle (h2, &s2); |
370 |
|
371 |
u_close (h2); |
372 |
|
373 |
if (!success) |
374 |
break; |
375 |
|
376 |
success = s1.dwVolumeSerialNumber == s2.dwVolumeSerialNumber |
377 |
&& s1.nFileIndexHigh == s2.nFileIndexHigh |
378 |
&& s1.nFileIndexLow == s2.nFileIndexLow; |
379 |
} |
380 |
#else |
381 |
struct stat s1, s2; |
382 |
|
383 |
if (fstat (h, &s1) || stat (path, &s2)) |
384 |
break; |
385 |
|
386 |
success = s1.st_dev == s2.st_dev |
387 |
&& s1.st_ino == s2.st_ino; |
388 |
#endif |
389 |
|
390 |
if (success) |
391 |
return h; // lock successfully acquired |
392 |
|
393 |
// files differ, close and retry - should be very rare |
394 |
u_close (h); |
395 |
} |
396 |
|
397 |
// failure |
398 |
u_close (h); |
399 |
return u_invalid_handle; |
400 |
} |
401 |
|