1 #include "rktio.h"
2 #include "rktio_private.h"
3 #include <errno.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <sys/stat.h>
7 #include <stdlib.h>
8 #ifdef RKTIO_SYSTEM_UNIX
9 # include <unistd.h>
10 # include <utime.h>
11 # include <fcntl.h>
12 # include <pwd.h>
13 # include <grp.h>
14 # include <dirent.h>
15 # include <sys/time.h>
16 # include <sys/utsname.h>
17 #endif
18 #ifdef RKTIO_SYSTEM_WINDOWS
19 # include <windows.h>
20 # include <shlobj.h>
21 # include <direct.h>
22 # include <sys/stat.h>
23 # include <sys/utime.h>
24 # include <io.h>
25 #endif
26
27 #if defined(S_IFDIR) && !defined(S_ISDIR)
28 # define S_ISDIR(m) ((m) & S_IFDIR)
29 #endif
30 #if defined(S_IFREG) && !defined(S_ISREG)
31 # define S_ISREG(m) ((m) & S_IFREG)
32 #endif
33 #if defined(S_IFLNK) && !defined(S_ISLNK)
34 # define S_ISLNK(m) ((m) & S_IFLNK)
35 #endif
36 #if defined(_S_IFDIR) && !defined(S_ISDIR)
37 # define S_ISDIR(m) ((m) & _S_IFDIR)
38 #endif
39 #if defined(_S_IFREG) && !defined(S_ISREG)
40 # define S_ISREG(m) ((m) & _S_IFREG)
41 #endif
42
43 #ifdef RKTIO_SYSTEM_UNIX
44 # define A_PATH_SEP '/'
45 #endif
46 #ifdef RKTIO_SYSTEM_WINDOWS
47 # define A_PATH_SEP '\\'
48 # define IS_A_DOS_SEP(c) IS_A_SEP(c)
49 #endif
50 #define IS_A_SEP(c) ((c) == A_PATH_SEP)
51
52 #if defined(RKTIO_SYSTEM_UNIX) && !defined(NO_UNIX_USERS)
53 static int have_user_ids = 0;
54 static uid_t uid;
55 static uid_t euid;
56 static gid_t gid;
57 static gid_t egid;
58
59 #define GROUP_MEMBER_CACHE_STATE_UNUSED 0
60 #define GROUP_MEMBER_CACHE_STATE_IN 1
61 #define GROUP_MEMBER_CACHE_STATE_NOT_IN 2
62 typedef struct group_member_cache_entry_t {
63 int state;
64 gid_t gid;
65 uid_t uid;
66 } group_member_cache_entry_t;
67 # define GROUP_CACHE_SIZE 10
68 #endif
69
70 #ifdef RKTIO_SYSTEM_WINDOWS
71 static int procs_inited = 0;
72 typedef BOOLEAN (WINAPI*CreateSymbolicLinkProc_t)(wchar_t *dest, wchar_t *src, DWORD flags);
73 static CreateSymbolicLinkProc_t CreateSymbolicLinkProc = NULL;
74
75 typedef BOOL (WINAPI*DeviceIoControlProc_t)(HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer,
76 DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize,
77 LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped);
78 static DeviceIoControlProc_t DeviceIoControlProc;
79
80 typedef DWORD (WINAPI*GetFinalPathNameByHandle_t)(HANDLE hFile, wchar_t *lpszFilePath,
81 DWORD cchFilePath, DWORD dwFlags);
82 GetFinalPathNameByHandle_t GetFinalPathNameByHandleProc;
83
84 # define rktioFILE_NAME_NORMALIZED 0x0
85 #endif
86
87 #ifdef RKTIO_SYSTEM_WINDOWS
init_procs()88 static void init_procs()
89 {
90 if (!procs_inited) {
91 HMODULE hm;
92
93 procs_inited = 1;
94
95 hm = LoadLibraryW(L"kernel32.dll");
96
97 CreateSymbolicLinkProc = (CreateSymbolicLinkProc_t)GetProcAddress(hm, "CreateSymbolicLinkW");
98 DeviceIoControlProc = (DeviceIoControlProc_t)GetProcAddress(hm, "DeviceIoControl");
99 GetFinalPathNameByHandleProc = (GetFinalPathNameByHandle_t)GetProcAddress(hm, "GetFinalPathNameByHandleW");
100
101 FreeLibrary(hm);
102 }
103 }
104 #endif
105
106 #ifdef RKTIO_SYSTEM_WINDOWS
107 # define RKTIO_UNC_READ RKTIO_PERMISSION_READ
108 # define RKTIO_UNC_WRITE RKTIO_PERMISSION_WRITE
109 # define RKTIO_UNC_EXEC RKTIO_PERMISSION_EXEC
110
111 # define FIND_FIRST FindFirstFileW
112 # define FIND_NEXT FindNextFileW
113 # define FIND_CLOSE FindClose
114 # define FF_TYPE WIN32_FIND_DATAW
115 # define FF_HANDLE_TYPE HANDLE
116 # define FIND_FAILED(h) (h == INVALID_HANDLE_VALUE)
117 # define FF_A_RDONLY FILE_ATTRIBUTE_READONLY
118 # define FF_A_DIR FILE_ATTRIBUTE_DIRECTORY
119 # define FF_A_LINK 0x400
120 # define GET_FF_ATTRIBS(fd) (fd.dwFileAttributes)
121 # define GET_FF_MODDATE(fd) convert_date(&fd.ftLastWriteTime)
122 # define GET_FF_NAME(fd) fd.cFileName
123
124 # define MKDIR_NO_MODE_FLAG
125
126 #define is_drive_letter(c) (((unsigned char)c < 128) && isalpha((unsigned char)c))
127
convert_date(const FILETIME * ft)128 static time_t convert_date(const FILETIME *ft)
129 {
130 LONGLONG l, delta;
131 FILETIME ft2;
132 SYSTEMTIME st;
133 TIME_ZONE_INFORMATION tz;
134
135 /* GetFileAttributes incorrectly shifts for daylight saving. It
136 subtracts an hour to get to UTC when daylight saving is in effect
137 now, even when daylight saving was not in effect when the file
138 was saved. Counteract the difference. There's a race condition
139 here, because we might cross the daylight-saving boundary between
140 the time that GetFileAttributes runs and GetTimeZoneInformation
141 runs. Cross your fingers... */
142 FileTimeToLocalFileTime(ft, &ft2);
143 FileTimeToSystemTime(&ft2, &st);
144
145 delta = 0;
146 if (GetTimeZoneInformation(&tz) == TIME_ZONE_ID_DAYLIGHT) {
147 /* Daylight saving is in effect now, so there may be a bad
148 shift. Check the file's date. */
149 if (!rktio_system_time_is_dst(&st, NULL))
150 delta = ((tz.StandardBias - tz.DaylightBias) * 60);
151 }
152
153 l = ((((LONGLONG)ft->dwHighDateTime << 32) | ft->dwLowDateTime)
154 - (((LONGLONG)0x019DB1DE << 32) | 0xD53E8000));
155 l /= 10000000;
156 l += delta;
157
158 return (time_t)l;
159 }
160
161 typedef struct mz_REPARSE_DATA_BUFFER {
162 ULONG ReparseTag;
163 USHORT ReparseDataLength;
164 USHORT Reserved;
165 union {
166 struct {
167 USHORT SubstituteNameOffset;
168 USHORT SubstituteNameLength;
169 USHORT PrintNameOffset;
170 USHORT PrintNameLength;
171 ULONG Flags;
172 WCHAR PathBuffer[1];
173 } SymbolicLinkReparseBuffer;
174 struct {
175 USHORT SubstituteNameOffset;
176 USHORT SubstituteNameLength;
177 USHORT PrintNameOffset;
178 USHORT PrintNameLength;
179 WCHAR PathBuffer[1];
180 } MountPointReparseBuffer;
181 struct {
182 UCHAR DataBuffer[1];
183 } GenericReparseBuffer;
184 } u;
185 } mz_REPARSE_DATA_BUFFER;
186
187 #define mzFILE_FLAG_OPEN_REPARSE_POINT 0x200000
188 #define mzFSCTL_GET_REPARSE_POINT 0x900A8
189
UNC_readlink(rktio_t * rktio,const char * fn)190 static char *UNC_readlink(rktio_t *rktio, const char *fn)
191 {
192 HANDLE h;
193 DWORD got;
194 char *buffer;
195 int size = 1024;
196 mz_REPARSE_DATA_BUFFER *rp;
197 int len, off;
198 wchar_t *lk;
199 const wchar_t *wp;
200
201 init_procs();
202
203 if (!DeviceIoControlProc) return NULL;
204
205 wp = WIDE_PATH_temp(fn);
206 if (!wp) {
207 /* Treat invalid path as non-existent path */
208 return MSC_IZE(strdup)(fn);
209 }
210
211 h = CreateFileW(wp, FILE_READ_ATTRIBUTES,
212 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
213 OPEN_EXISTING,
214 FILE_FLAG_BACKUP_SEMANTICS | mzFILE_FLAG_OPEN_REPARSE_POINT,
215 NULL);
216
217 if (h == INVALID_HANDLE_VALUE) {
218 get_windows_error();
219 return NULL;
220 }
221
222 while (1) {
223 buffer = (char *)malloc(size);
224 if (DeviceIoControlProc(h, mzFSCTL_GET_REPARSE_POINT, NULL, 0, buffer, size,
225 &got, NULL))
226 break;
227 else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
228 size *= 2;
229 free(buffer);
230 buffer = (char *)malloc(size);
231 } else {
232 get_windows_error();
233 CloseHandle(h);
234 free(buffer);
235 return NULL;
236 }
237 }
238
239 CloseHandle(h);
240
241 rp = (mz_REPARSE_DATA_BUFFER *)buffer;
242 if ((rp->ReparseTag != IO_REPARSE_TAG_SYMLINK)
243 && (rp->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)) {
244 free(buffer);
245 set_racket_error(RKTIO_ERROR_LINK_FAILED);
246 return NULL;
247 }
248
249 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
250 off = rp->u.SymbolicLinkReparseBuffer.SubstituteNameOffset;
251 len = rp->u.SymbolicLinkReparseBuffer.SubstituteNameLength;
252 if (!len) {
253 off = rp->u.SymbolicLinkReparseBuffer.PrintNameOffset;
254 len = rp->u.SymbolicLinkReparseBuffer.PrintNameLength;
255 }
256 } else {
257 off = rp->u.MountPointReparseBuffer.SubstituteNameOffset;
258 len = rp->u.MountPointReparseBuffer.SubstituteNameLength;
259 if (!len) {
260 off = rp->u.MountPointReparseBuffer.PrintNameOffset;
261 len = rp->u.MountPointReparseBuffer.PrintNameLength;
262 }
263 }
264
265 lk = (wchar_t *)malloc(len + 2);
266
267 if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
268 memcpy(lk, (char *)rp->u.SymbolicLinkReparseBuffer.PathBuffer + off, len);
269 else
270 memcpy(lk, (char *)rp->u.MountPointReparseBuffer.PathBuffer + off, len);
271
272 lk[len>>1] = 0;
273
274 if ((lk[0] == '\\') && (lk[1] == '?') && (lk[2] == '?') && (lk[3] == '\\')) {
275 /* "?\\" is a prefix that means "unparsed", or something like that */
276 memmove(lk, lk+4, len - 8);
277 len -= 8;
278 lk[len>>1] = 0;
279
280 if ((lk[0] == 'U') && (lk[1] == 'N') && (lk[2] == 'C') && (lk[3] == '\\')) {
281 /* "UNC\" is a further prefix that means "UNC"; replace "UNC" with "\" */
282 memmove(lk, lk+2, len - 4);
283 lk[0] = '\\';
284 len -= 4;
285 lk[len>>1] = 0;
286 }
287 }
288
289 free(buffer);
290
291 /* Make sure it's not empty, because that would form a bad path: */
292 if (!lk[0]) {
293 free(lk);
294 set_racket_error(RKTIO_ERROR_LINK_FAILED);
295 return NULL;
296 }
297
298 buffer = NARROW_PATH_copy(lk);
299 free(lk);
300
301 return buffer;
302 }
303
UNC_stat(rktio_t * rktio,const char * dirname,int * flags,int * isdir,int * islink,rktio_timestamp_t ** date,rktio_filesize_t * filesize,const char ** resolved_path,int set_flags)304 static int UNC_stat(rktio_t *rktio, const char *dirname, int *flags, int *isdir, int *islink,
305 rktio_timestamp_t **date, rktio_filesize_t *filesize,
306 const char **resolved_path, int set_flags)
307 /* dirname must be absolute */
308 {
309 /* Note: stat() doesn't work with UNC "drive" names or \\?\ paths.
310 Also, stat() doesn't distinguish between the ability to
311 list a directory's content and whether the directory exists.
312 So, we use GetFileAttributesExW(). */
313 char *copy;
314 WIN32_FILE_ATTRIBUTE_DATA fad;
315 int len, must_be_dir = 0;
316 int same_path = 0; /* to give up on cyclic links */
317 const wchar_t *wp;
318
319 if (resolved_path)
320 *resolved_path = NULL;
321
322 retry:
323
324 if (islink)
325 *islink = 0;
326 if (isdir)
327 *isdir = 0;
328 if (date)
329 *date = NULL;
330
331 len = strlen(dirname);
332
333 copy = malloc(len+3); /* leave room to add `\.` */
334 memcpy(copy, dirname, len+1);
335
336 if (!rktio->windows_nt_or_later
337 || ((len >= 4)
338 && (copy[0] == '\\')
339 && (copy[1] == '\\')
340 && (copy[2] == '?')
341 && (copy[3] == '\\'))) {
342 /* Keep `copy` as-is */
343 } else {
344 /* Strip trailing separators */
345 while (IS_A_DOS_SEP(copy[len - 1])) {
346 --len;
347 copy[len] = 0;
348 must_be_dir = 1;
349 }
350 }
351
352 /* If we ended up with "\\?\X:" (and nothing after), then drop the "\\?\" */
353 if ((copy[0] == '\\')&& (copy[1] == '\\') && (copy[2] == '?') && (copy[3] == '\\')
354 && is_drive_letter(copy[4]) && (copy[5] == ':') && !copy[6]) {
355 memmove(copy, copy + 4, len - 4);
356 len -= 4;
357 copy[len] = 0;
358 }
359 /* If we ended up with "\\?\\X:" (and nothing after), then drop the "\\?\\" */
360 if ((copy[0] == '\\') && (copy[1] == '\\') && (copy[2] == '?') && (copy[3] == '\\')
361 && (copy[4] == '\\') && is_drive_letter(copy[5]) && (copy[6] == ':') && !copy[7]) {
362 memmove(copy, copy + 5, len - 5);
363 len -= 5;
364 copy[len] = 0;
365 }
366 /* If we ended up with "X:[/]", then add a "." at the end so that we get information
367 for the drive, not the current directory of the drive: */
368 if (is_drive_letter(copy[0]) && (copy[1] == ':')
369 && (!copy[2]
370 || (IS_A_DOS_SEP(copy[2]) && !copy[3]))) {
371 copy[2] = '\\';
372 copy[3] = '.';
373 copy[4] = 0;
374 }
375
376 wp = WIDE_PATH_temp(copy);
377 if (!wp) {
378 /* Treat invalid path as non-existent; `WIDE_PATH_temp` set the error */
379 free(copy);
380 return 0;
381 }
382
383 if (!GetFileAttributesExW(wp, GetFileExInfoStandard, &fad)) {
384 get_windows_error();
385 free(copy);
386 return 0;
387 } else {
388 if ((GET_FF_ATTRIBS(fad) & FF_A_LINK) && !same_path) {
389 if (islink) {
390 *islink = 1;
391 if (isdir)
392 *isdir = (GET_FF_ATTRIBS(fad) & FF_A_DIR);
393 return 1;
394 } else {
395 /* Resolve a link by opening the link and then getting
396 the path from the handle. */
397 HANDLE h;
398 wchar_t *dest = NULL;
399 DWORD len = 255, dest_len;
400
401 h = CreateFileW(WIDE_PATH_temp(copy), FILE_READ_ATTRIBUTES,
402 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
403 OPEN_EXISTING,
404 FILE_FLAG_BACKUP_SEMANTICS,
405 NULL);
406
407 if (!h) {
408 get_windows_error();
409 free(copy);
410 return 0;
411 }
412
413 do {
414 init_procs();
415 if (dest) free(dest);
416 dest_len = len + 1;
417 dest = malloc(dest_len);
418 len = GetFinalPathNameByHandleProc(h, dest, dest_len, rktioFILE_NAME_NORMALIZED);
419 } while (len > dest_len);
420
421 if (!len) {
422 get_windows_error();
423 CloseHandle(h);
424 free(copy);
425 free(dest);
426 return 0;
427 }
428
429 CloseHandle(h);
430
431 dirname = NARROW_PATH_copy(dest);
432 if (resolved_path)
433 *resolved_path = dirname;
434
435 same_path = !strcmp(dirname, copy);
436
437 free(dest);
438 free(copy);
439
440 goto retry;
441 }
442 }
443
444 if (set_flags != -1) {
445 DWORD attrs = GET_FF_ATTRIBS(fad);
446
447 if (!(set_flags & RKTIO_UNC_WRITE))
448 attrs |= FF_A_RDONLY;
449 else if (attrs & FF_A_RDONLY)
450 attrs -= FF_A_RDONLY;
451
452 if (!SetFileAttributesW(WIDE_PATH_temp(copy), attrs)) {
453 get_windows_error();
454 free(copy);
455 return 0;
456 }
457 } else {
458 if (must_be_dir && !(GET_FF_ATTRIBS(fad) & FF_A_DIR)) {
459 set_racket_error(RKTIO_ERROR_NOT_A_DIRECTORY);
460 free(copy);
461 return 0;
462 }
463 if (flags)
464 *flags = RKTIO_UNC_READ | RKTIO_UNC_EXEC | ((GET_FF_ATTRIBS(fad) & FF_A_RDONLY) ? 0 : RKTIO_UNC_WRITE);
465 if (date) {
466 rktio_timestamp_t *dt;
467 time_t mdt;
468 mdt = GET_FF_MODDATE(fad);
469 dt = malloc(sizeof(rktio_timestamp_t));
470 *dt = (rktio_timestamp_t)mdt;
471 *date = dt;
472 }
473 if (isdir) {
474 *isdir = (GET_FF_ATTRIBS(fad) & FF_A_DIR);
475 }
476 if (filesize) {
477 *filesize = ((rktio_filesize_t)fad.nFileSizeHigh << 32) + fad.nFileSizeLow;
478 }
479 }
480 free(copy);
481 return 1;
482 }
483 }
484 #endif
485
rktio_file_exists(rktio_t * rktio,const char * filename)486 int rktio_file_exists(rktio_t *rktio, const char *filename)
487 /* Windows: check for special filenames before calling */
488 {
489 # ifdef NO_STAT_PROC
490 int fd;
491
492 fd = open(filename, O_RDONLY);
493 if (fd != -1) {
494 fclose(fd);
495 return 1;
496 } else
497 return 0;
498 # else
499 # ifdef RKTIO_SYSTEM_WINDOWS
500 {
501 int isdir;
502 return (UNC_stat(rktio, filename, NULL, &isdir, NULL, NULL, NULL, NULL, -1)
503 && !isdir);
504 }
505 # else
506 struct MSC_IZE(stat) buf;
507 int ok;
508
509 do {
510 ok = MSC_W_IZE(stat)(MSC_WIDE_PATH_temp(filename), &buf);
511 } while ((ok == -1) && (errno == EINTR));
512
513 return !ok && !S_ISDIR(buf.st_mode);
514 # endif
515 # endif
516 }
517
rktio_directory_exists(rktio_t * rktio,const char * dirname)518 int rktio_directory_exists(rktio_t *rktio, const char *dirname)
519 {
520 # ifdef NO_STAT_PROC
521 return 0;
522 # else
523 # ifdef RKTIO_SYSTEM_WINDOWS
524 int isdir;
525
526 return (UNC_stat(rktio, dirname, NULL, &isdir, NULL, NULL, NULL, NULL, -1)
527 && isdir);
528 # else
529 struct MSC_IZE(stat) buf;
530
531 while (1) {
532 if (!MSC_IZE(stat)(dirname, &buf))
533 break;
534 else if (errno != EINTR)
535 return 0;
536 }
537
538 return S_ISDIR(buf.st_mode);
539 # endif
540 # endif
541 }
542
rktio_is_regular_file(rktio_t * rktio,const char * filename)543 int rktio_is_regular_file(rktio_t *rktio, const char *filename)
544 /* Windows: check for special filenames before calling */
545 {
546 # ifdef NO_STAT_PROC
547 return 0;
548 # else
549 struct MSC_IZE(stat) buf;
550 const WIDE_PATH_t *wp;
551
552 wp = MSC_WIDE_PATH_temp(filename);
553 if (!wp) return 0;
554
555 while (1) {
556 if (!MSC_W_IZE(stat)(wp, &buf))
557 break;
558 else if (errno != EINTR)
559 return 0;
560 }
561
562 return S_ISREG(buf.st_mode);
563 # endif
564 }
565
rktio_link_exists(rktio_t * rktio,const char * filename)566 int rktio_link_exists(rktio_t *rktio, const char *filename)
567 {
568 #ifdef RKTIO_SYSTEM_WINDOWS
569 {
570 int islink;
571 if (UNC_stat(rktio, filename, NULL, NULL, &islink, NULL, NULL, NULL, -1)
572 && islink)
573 return 1;
574 else
575 return 0;
576 }
577 #else
578 {
579 struct MSC_IZE(stat) buf;
580 while (1) {
581 if (!MSC_W_IZE(lstat)(MSC_WIDE_PATH_temp(filename), &buf))
582 break;
583 else if (errno != EINTR)
584 return 0;
585 }
586
587 if (S_ISLNK(buf.st_mode))
588 return 1;
589 else
590 return 0;
591 }
592 #endif
593 }
594
rktio_file_type(rktio_t * rktio,rktio_const_string_t filename)595 int rktio_file_type(rktio_t *rktio, rktio_const_string_t filename)
596 /* Windows: check for special filenames before calling */
597 {
598 #ifdef RKTIO_SYSTEM_WINDOWS
599 {
600 int islink, isdir;
601 if (UNC_stat(rktio, filename, NULL, &isdir, &islink, NULL, NULL, NULL, -1)) {
602 if (islink) {
603 if (isdir)
604 return RKTIO_FILE_TYPE_DIRECTORY_LINK;
605 else
606 return RKTIO_FILE_TYPE_LINK;
607 } else if (isdir)
608 return RKTIO_FILE_TYPE_DIRECTORY;
609 else
610 return RKTIO_FILE_TYPE_FILE;
611 } else
612 return RKTIO_FILE_TYPE_ERROR;
613 }
614 #else
615 {
616 struct MSC_IZE(stat) buf;
617 while (1) {
618 if (!MSC_W_IZE(lstat)(MSC_WIDE_PATH_temp(filename), &buf))
619 break;
620 else if (errno != EINTR)
621 return RKTIO_FILE_TYPE_ERROR;
622 }
623
624 if (S_ISLNK(buf.st_mode))
625 return RKTIO_FILE_TYPE_LINK;
626 else if (S_ISDIR(buf.st_mode))
627 return RKTIO_FILE_TYPE_DIRECTORY;
628 else
629 return RKTIO_FILE_TYPE_FILE;
630 }
631 #endif
632
633 }
634
rktio_get_current_directory(rktio_t * rktio)635 char *rktio_get_current_directory(rktio_t *rktio)
636 {
637 #ifdef RKTIO_SYSTEM_WINDOWS
638 int need_l, bl = 256;
639 wchar_t *wbuf;
640 char *r;
641
642 wbuf = malloc(bl * sizeof(wchar_t));
643 while (1) {
644 need_l = GetCurrentDirectoryW(bl, wbuf);
645 if (need_l > bl) {
646 free(wbuf);
647 wbuf = malloc(need_l * sizeof(wchar_t));
648 bl = need_l;
649 } else
650 break;
651 }
652
653 if (!need_l) {
654 get_windows_error();
655 return NULL;
656 }
657
658 r = NARROW_PATH_copy_then_free(wbuf);
659
660 return r;
661 #else
662 char *r, *s;
663 int len = 256;
664
665 s = malloc(len);
666 while (1) {
667 r = MSC_IZE(getcwd)(s, len);
668 if (r)
669 break;
670 if (errno == ERANGE) {
671 free(s);
672 len *= 2;
673 s = malloc(len);
674 } else
675 break;
676 }
677 if (!r) {
678 free(s);
679 get_posix_error();
680 }
681 return r;
682 #endif
683 }
684
rktio_set_current_directory(rktio_t * rktio,const char * path)685 rktio_ok_t rktio_set_current_directory(rktio_t *rktio, const char *path)
686 {
687 int err;
688 const WIDE_PATH_t *wp;
689
690 wp = MSC_WIDE_PATH_temp(path);
691 if (!wp) return 0;
692
693 while (1) {
694 err = MSC_W_IZE(chdir)(wp);
695 if (!err || (errno != EINTR))
696 break;
697 }
698
699 get_posix_error();
700
701 return !err;
702 }
703
get_identity(rktio_t * rktio,rktio_fd_t * fd,const char * path,int follow_links)704 static rktio_identity_t *get_identity(rktio_t *rktio, rktio_fd_t *fd, const char *path, int follow_links)
705 {
706 uintptr_t devi = 0, inoi = 0, inoi2 = 0;
707 int devi_bits = 0, inoi_bits = 0, inoi2_bits = 0;
708
709 #ifdef RKTIO_SYSTEM_UNIX
710 int errid = 0;
711 struct MSC_IZE(stat) buf;
712
713 while (1) {
714 if (!path && !MSC_IZE(fstat)(rktio_fd_system_fd(rktio, fd), &buf))
715 break;
716 else if (path && follow_links && !MSC_IZE(stat)(path, &buf))
717 break;
718 else if (path && !follow_links && !MSC_IZE(lstat)(path, &buf))
719 break;
720 else if (errno != EINTR) {
721 errid = errno;
722 break;
723 }
724 }
725
726 if (errid) {
727 get_posix_error();
728 return NULL;
729 } else {
730 /* Warning: we assume that dev_t and ino_t fit in a pointer-sized integer. */
731 devi = (uintptr_t)buf.st_dev;
732 inoi = (uintptr_t)buf.st_ino;
733 devi_bits = sizeof(buf.st_dev) << 3;
734 inoi_bits = sizeof(buf.st_ino) << 3;
735 }
736 #endif
737 #ifdef RKTIO_SYSTEM_WINDOWS
738 BY_HANDLE_FILE_INFORMATION info;
739 HANDLE fdh;
740
741 init_procs();
742
743 if (path) {
744 const wchar_t *wp;
745 wp = WIDE_PATH_temp(path);
746 if (!wp) return 0;
747 fdh = CreateFileW(wp,
748 0, /* not even read access => just get info */
749 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
750 NULL,
751 OPEN_EXISTING,
752 FILE_FLAG_BACKUP_SEMANTICS
753 | ((fd && CreateSymbolicLinkProc)
754 ? mzFILE_FLAG_OPEN_REPARSE_POINT
755 : 0),
756 NULL);
757 if (fdh == INVALID_HANDLE_VALUE) {
758 get_windows_error();
759 return NULL;
760 }
761 } else
762 fdh = (HANDLE)rktio_fd_system_fd(rktio, fd);
763
764 if (!GetFileInformationByHandle(fdh, &info)) {
765 get_windows_error();
766 if (path) CloseHandle(fdh);
767 return NULL;
768 }
769
770 if (path) CloseHandle(fdh);
771
772 devi = info.dwVolumeSerialNumber;
773 inoi = info.nFileIndexLow;
774 inoi2 = info.nFileIndexHigh;
775
776 devi_bits = 32;
777 inoi_bits = 32;
778 inoi2_bits = 32;
779 #endif
780
781 {
782 rktio_identity_t *id;
783
784 id = malloc(sizeof(rktio_identity_t));
785
786 id->a = devi;
787 id->a_bits = devi_bits;
788 id->b = inoi;
789 id->b_bits = inoi_bits;
790 id->c = inoi2;
791 id->c_bits = inoi2_bits;
792
793 return id;
794 }
795 }
796
rktio_fd_identity(rktio_t * rktio,rktio_fd_t * fd)797 rktio_identity_t *rktio_fd_identity(rktio_t *rktio, rktio_fd_t *fd)
798 {
799 return get_identity(rktio, fd, NULL, 0);
800 }
801
rktio_path_identity(rktio_t * rktio,const char * path,int follow_links)802 rktio_identity_t *rktio_path_identity(rktio_t *rktio, const char *path, int follow_links)
803 {
804 return get_identity(rktio, NULL, path, follow_links);
805 }
806
807 #ifdef RKTIO_SYSTEM_WINDOWS
enable_write_permission(rktio_t * rktio,const char * fn)808 static int enable_write_permission(rktio_t *rktio, const char *fn)
809 {
810 int flags;
811
812 return UNC_stat(rktio, fn, &flags, NULL, NULL, NULL, NULL, NULL, RKTIO_UNC_WRITE);
813 }
814 #endif
815
rktio_delete_file(rktio_t * rktio,const char * fn,int enable_write_on_fail)816 int rktio_delete_file(rktio_t *rktio, const char *fn, int enable_write_on_fail)
817 {
818 #ifdef RKTIO_SYSTEM_WINDOWS
819 int errid;
820 const wchar_t *wp;
821
822 wp = WIDE_PATH_temp(fn);
823 if (!wp) return 0;
824
825 if (DeleteFileW(wp))
826 return 1;
827
828 errid = GetLastError();
829 if ((errid == ERROR_ACCESS_DENIED) && enable_write_on_fail) {
830 /* Maybe it's just that the file has no write permission. Provide a more
831 Unix-like experience by attempting to change the file's permission. */
832 if (enable_write_permission(rktio, fn)) {
833 if (DeleteFileW(WIDE_PATH_temp(fn)))
834 return 1;
835 }
836 }
837
838 get_windows_error();
839 return 0;
840 #else
841 while (1) {
842 if (!MSC_W_IZE(unlink)(MSC_WIDE_PATH_temp(fn)))
843 return 1;
844 else if (errno != EINTR)
845 break;
846 }
847
848 get_posix_error();
849 return 0;
850 #endif
851 }
852
rktio_rename_file(rktio_t * rktio,const char * dest,const char * src,int exists_ok)853 int rktio_rename_file(rktio_t *rktio, const char *dest, const char *src, int exists_ok)
854 {
855 #ifdef RKTIO_SYSTEM_WINDOWS
856 int errid;
857 wchar_t *src_w;
858 const wchar_t *dest_w;
859
860 src_w = WIDE_PATH_copy(src);
861 if (!src_w) return 0;
862
863 dest_w = WIDE_PATH_temp(dest);
864 if (!dest_w) return 0;
865
866 if (MoveFileExW(src_w, dest_w, (exists_ok ? MOVEFILE_REPLACE_EXISTING : 0))) {
867 free(src_w);
868 return 1;
869 }
870
871 errid = GetLastError();
872
873 if (errid == ERROR_CALL_NOT_IMPLEMENTED) {
874 /* Then we have the great misfortune of running in Windows 9x. If
875 exists_ok, then do something no less stupid than the OS
876 itself: */
877 if (exists_ok)
878 MSC_W_IZE(unlink)(MSC_WIDE_PATH_temp(dest));
879 if (MoveFileW(src_w, WIDE_PATH_temp(dest))) {
880 free(src_w);
881 return 1;
882 }
883 get_windows_error();
884 } else if (errid == ERROR_ALREADY_EXISTS) {
885 set_racket_error(RKTIO_ERROR_EXISTS);
886 return 0;
887 } else
888 get_windows_error();
889
890 free(src_w);
891 return 0;
892 #else
893 if (!exists_ok && (rktio_file_exists(rktio, dest) || rktio_directory_exists(rktio, dest))) {
894 /* We use a specialized error here, because it's not
895 a system error (e.g., setting `errval` to `EEXIST` would
896 be a lie). */
897 set_racket_error(RKTIO_ERROR_EXISTS);
898 return 0;
899 }
900
901 while (1) {
902 if (!rename(src, dest))
903 return 1;
904 else if (errno != EINTR)
905 break;
906 }
907
908 get_posix_error();
909 return 0;
910 #endif
911 }
912
rktio_readlink(rktio_t * rktio,const char * fullfilename)913 char *rktio_readlink(rktio_t *rktio, const char *fullfilename)
914 /* fullfilename must not have a trailing separator */
915 {
916 #ifdef RKTIO_SYSTEM_WINDOWS
917 int is_link;
918 if (UNC_stat(rktio, fullfilename, NULL, NULL, &is_link, NULL, NULL, NULL, -1)
919 && is_link) {
920 return UNC_readlink(rktio, fullfilename);
921 } else {
922 set_racket_error(RKTIO_ERROR_NOT_A_LINK);
923 return NULL;
924 }
925 #else
926 int len, buf_len = 256;
927 char *buffer = malloc(buf_len);
928
929 while (1) {
930 len = readlink(fullfilename, buffer, buf_len);
931 if (len == -1) {
932 if (errno != EINTR) {
933 if (errno == EINVAL)
934 set_racket_error(RKTIO_ERROR_NOT_A_LINK);
935 else
936 get_posix_error();
937 free(buffer);
938 return NULL;
939 }
940 } else if (len == buf_len) {
941 /* maybe too small */
942 free(buffer);
943 buf_len *= 2;
944 buffer = malloc(buf_len);
945 } else
946 break;
947 }
948 buffer[len] = 0;
949 return buffer;
950 #endif
951 }
952
rktio_make_directory(rktio_t * rktio,const char * filename)953 int rktio_make_directory(rktio_t *rktio, const char *filename)
954 {
955 #ifdef NO_MKDIR
956 set_racket_error(RKTIO_ERROR_UNSUPPORTED);
957 return 0;
958 #else
959 int len;
960 char *copied = NULL;
961 const WIDE_PATH_t *wp;
962
963 /* Make sure path doesn't have trailing separator: */
964 len = strlen(filename);
965 while (len && IS_A_SEP(filename[len - 1])) {
966 if (!copied)
967 copied = MSC_IZE(strdup)(filename);
968 copied[--len] = 0;
969 filename = copied;
970 }
971
972 while (1) {
973 wp = MSC_WIDE_PATH_temp(filename);
974 if (!wp) return 0;
975 if (!MSC_W_IZE(mkdir)(wp
976 # ifndef MKDIR_NO_MODE_FLAG
977 , 0777
978 # endif
979 )) {
980 if (copied) free(copied);
981 return 1;
982 } else if (errno != EINTR)
983 break;
984 }
985
986 if (errno == EEXIST)
987 set_racket_error(RKTIO_ERROR_EXISTS);
988 else
989 get_posix_error();
990
991 if (copied) free(copied);
992
993 return 0;
994 }
995
996 int rktio_delete_directory(rktio_t *rktio, const char *filename, const char *current_directory, int enable_write_on_fail)
997 {
998 #ifdef RKTIO_SYSTEM_WINDOWS
999 int tried_cwd = 0, tried_perm = 0;
1000 #endif
1001 const WIDE_PATH_t *wp;
1002
1003 while (1) {
1004 wp = MSC_WIDE_PATH_temp(filename);
1005 if (!wp) return 0;
1006 if (!MSC_W_IZE(rmdir)(wp))
1007 return 1;
1008 # ifdef RKTIO_SYSTEM_WINDOWS
1009 else if ((errno == EACCES) && !tried_cwd) {
1010 /* Maybe we're using the target directory. Try a real setcwd. */
1011 (void)rktio_set_current_directory(rktio, current_directory);
1012 tried_cwd = 1;
1013 } else if ((errno == EACCES) && !tried_perm && enable_write_on_fail) {
1014 /* Maybe the directory doesn't have write permission. */
1015 (void)enable_write_permission(rktio, filename);
1016 tried_perm = 1;
1017 }
1018 # endif
1019 else if (errno != EINTR)
1020 break;
1021 }
1022
1023 get_posix_error();
1024 return 0;
1025 #endif
1026 }
1027
rktio_make_link(rktio_t * rktio,const char * src,const char * dest,int dest_is_directory)1028 int rktio_make_link(rktio_t *rktio, const char *src, const char *dest, int dest_is_directory)
1029 /* `src` is the file that is written, and `dest` is written to that
1030 file */
1031 {
1032 #if defined(RKTIO_SYSTEM_WINDOWS)
1033 init_procs();
1034
1035 # ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
1036 # define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
1037 # endif
1038 # ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
1039 # define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x2
1040 # endif
1041
1042 if (CreateSymbolicLinkProc) {
1043 int flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
1044 wchar_t *src_w;
1045 wchar_t *dest_w;
1046
1047 if (dest_is_directory)
1048 flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; /* directory */
1049
1050 src_w = WIDE_PATH_copy(src);
1051 if (!src_w) return 0;
1052
1053 dest_w = WIDE_PATH_temp(dest);
1054 if (!dest_w) return 0;
1055
1056 if (CreateSymbolicLinkProc(src_w, dest_w, flags)) {
1057 free(src_w);
1058 return 1;
1059 }
1060 if (GetLastError() == ERROR_ALREADY_EXISTS)
1061 set_racket_error(RKTIO_ERROR_EXISTS);
1062 else
1063 get_windows_error();
1064 free(src_w);
1065 } else
1066 set_racket_error(RKTIO_ERROR_UNSUPPORTED);
1067
1068 return 0;
1069 #else
1070 while (1) {
1071 if (!symlink(dest, src))
1072 return 1;
1073 else if (errno != EINTR)
1074 break;
1075 }
1076 if (errno == EEXIST)
1077 set_racket_error(RKTIO_ERROR_EXISTS);
1078 else
1079 get_posix_error();
1080 return 0;
1081 #endif
1082 }
1083
rktio_get_file_modify_seconds(rktio_t * rktio,const char * file)1084 rktio_timestamp_t *rktio_get_file_modify_seconds(rktio_t *rktio, const char *file)
1085 {
1086 #ifdef RKTIO_SYSTEM_WINDOWS
1087 rktio_timestamp_t *secs;
1088
1089 if (UNC_stat(rktio, file, NULL, NULL, NULL, &secs, NULL, NULL, -1))
1090 return secs;
1091 return NULL;
1092 #else
1093 struct MSC_IZE(stat) buf;
1094
1095 while (1) {
1096 if (!MSC_W_IZE(stat)(MSC_WIDE_PATH_temp(file), &buf)){
1097 rktio_timestamp_t *ts = malloc(sizeof(rktio_timestamp_t));
1098 *ts = buf.st_mtime;
1099 return ts;
1100 }
1101 if (errno != EINTR)
1102 break;
1103 }
1104
1105 get_posix_error();
1106 return NULL;
1107 #endif
1108 }
1109
rktio_set_file_modify_seconds(rktio_t * rktio,const char * file,rktio_timestamp_t secs)1110 int rktio_set_file_modify_seconds(rktio_t *rktio, const char *file, rktio_timestamp_t secs)
1111 {
1112 while (1) {
1113 struct MSC_IZE(utimbuf) ut;
1114 const WIDE_PATH_t *wp;
1115 ut.actime = secs;
1116 ut.modtime = secs;
1117 wp = MSC_WIDE_PATH_temp(file);
1118 if (!wp) return 0;
1119 if (!MSC_W_IZE(utime)(wp, &ut))
1120 return 1;
1121 if (errno != EINTR)
1122 break;
1123 }
1124
1125 get_posix_error();
1126 return 0;
1127 }
1128
1129 #if defined(RKTIO_SYSTEM_UNIX) && !defined(NO_UNIX_USERS)
user_in_group(rktio_t * rktio,uid_t uid,gid_t gid)1130 static int user_in_group(rktio_t *rktio, uid_t uid, gid_t gid)
1131 {
1132 struct group *g;
1133 struct passwd *pw;
1134 int i, in;
1135
1136 if (!rktio->group_member_cache)
1137 rktio->group_member_cache = calloc(GROUP_CACHE_SIZE, sizeof(group_member_cache_entry_t));
1138
1139 for (i = 0; i < GROUP_CACHE_SIZE; i++) {
1140 if ((rktio->group_member_cache[i].state != GROUP_MEMBER_CACHE_STATE_UNUSED)
1141 && (rktio->group_member_cache[i].gid == gid)
1142 && (rktio->group_member_cache[i].uid == uid))
1143 return (rktio->group_member_cache[i].state == GROUP_MEMBER_CACHE_STATE_IN);
1144 }
1145
1146 pw = getpwuid(uid);
1147 if (!pw)
1148 return 0;
1149
1150 g = getgrgid(gid);
1151 if (!g)
1152 return 0;
1153
1154 for (i = 0; g->gr_mem[i]; i++) {
1155 if (!strcmp(g->gr_mem[i], pw->pw_name))
1156 break;
1157 }
1158
1159 in = !!(g->gr_mem[i]);
1160
1161 for (i = 0; i < GROUP_CACHE_SIZE; i++) {
1162 if (rktio->group_member_cache[i].state == GROUP_MEMBER_CACHE_STATE_UNUSED) {
1163 rktio->group_member_cache[i].gid = gid;
1164 rktio->group_member_cache[i].uid = uid;
1165 rktio->group_member_cache[i].state = (in
1166 ? GROUP_MEMBER_CACHE_STATE_IN
1167 : GROUP_MEMBER_CACHE_STATE_NOT_IN);
1168 break;
1169 }
1170 }
1171
1172 return in;
1173 }
1174 #endif
1175
rktio_get_file_or_directory_permissions(rktio_t * rktio,const char * filename,int all_bits)1176 int rktio_get_file_or_directory_permissions(rktio_t *rktio, const char *filename, int all_bits)
1177 /* -1 result indicates an error */
1178 {
1179 # ifdef NO_STAT_PROC
1180 set_racket_error(RKTIO_ERROR_UNSUPPORTED);
1181 return RKTIO_PERMISSION_ERROR;
1182 # else
1183 # ifdef RKTIO_SYSTEM_UNIX
1184 /* General strategy for permissions (to deal with setuid)
1185 taken from euidaccess() in coreutils... */
1186 # ifndef NO_UNIX_USERS
1187 if (have_user_ids == 0) {
1188 have_user_ids = 1;
1189 uid = getuid();
1190 gid = getgid();
1191 euid = geteuid();
1192 egid = getegid();
1193 }
1194
1195 if (!all_bits && (uid == euid) && (gid == egid)) {
1196 /* Not setuid; use access() */
1197 int read, write, execute, ok;
1198
1199 do {
1200 ok = access(filename, R_OK);
1201 } while ((ok == -1) && (errno == EINTR));
1202 read = !ok;
1203
1204 if (ok && (errno != EACCES)) {
1205 get_posix_error();
1206 return RKTIO_PERMISSION_ERROR;
1207 } else {
1208 do {
1209 ok = access(filename, W_OK);
1210 } while ((ok == -1) && (errno == EINTR));
1211 write = !ok;
1212
1213 /* Don't fail at the exec step if errno is EPERM; under Mac OS
1214 X, at least, such a failure seems to mean that the file is
1215 not writable. (We assume it's not a directory-access issue,
1216 since the read test succeeded.) */
1217 if (ok && (errno != EACCES) && (errno != EPERM) && (errno != EROFS)) {
1218 get_posix_error();
1219 return RKTIO_PERMISSION_ERROR;
1220 } else {
1221 do {
1222 ok = access(filename, X_OK);
1223 } while ((ok == -1) && (errno == EINTR));
1224 execute = !ok;
1225
1226 /* Don't fail at the exec step if errno is EPERM; under Mac OS
1227 X, at least, such a failure simply means that the file is
1228 not executable. */
1229 if (ok && (errno != EACCES) && (errno != EPERM)) {
1230 get_posix_error();
1231 return RKTIO_PERMISSION_ERROR;
1232 } else {
1233 return ((read ? RKTIO_PERMISSION_READ : 0)
1234 | (write ? RKTIO_PERMISSION_WRITE : 0)
1235 | (execute ? RKTIO_PERMISSION_EXEC : 0));
1236 }
1237 }
1238 }
1239 }
1240 # endif
1241 {
1242 /* Use stat, because setuid, or because or no user info available */
1243 struct stat buf;
1244 int cr, read, write, execute;
1245
1246 do {
1247 cr = stat(filename, &buf);
1248 } while ((cr == -1) && (errno == EINTR));
1249
1250 if (cr) {
1251 get_posix_error();
1252 return RKTIO_PERMISSION_ERROR;
1253 } else {
1254 if (all_bits) {
1255 int bits = buf.st_mode;
1256 # ifdef S_IFMT
1257 bits -= (bits & S_IFMT);
1258 # endif
1259 return bits;
1260 } else {
1261 # ifndef NO_UNIX_USERS
1262 if (euid == 0) {
1263 /* Super-user can read/write anything, and can
1264 execute anything that someone can execute */
1265 read = 1;
1266 write = 1;
1267 execute = !!(buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH));
1268 } else if (buf.st_uid == euid) {
1269 read = !!(buf.st_mode & S_IRUSR);
1270 write = !!(buf.st_mode & S_IWUSR);
1271 execute = !!(buf.st_mode & S_IXUSR);
1272 } else if ((egid == buf.st_gid) || user_in_group(rktio, euid, buf.st_gid)) {
1273 read = !!(buf.st_mode & S_IRGRP);
1274 write = !!(buf.st_mode & S_IWGRP);
1275 execute = !!(buf.st_mode & S_IXGRP);
1276 } else {
1277 read = !!(buf.st_mode & S_IROTH);
1278 write = !!(buf.st_mode & S_IWOTH);
1279 execute = !!(buf.st_mode & S_IXOTH);
1280 }
1281 # else
1282 read = !!(buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
1283 write = !!(buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH));
1284 execute = !!(buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH));
1285 # endif
1286
1287 return ((read ? RKTIO_PERMISSION_READ : 0)
1288 | (write ? RKTIO_PERMISSION_WRITE : 0)
1289 | (execute ? RKTIO_PERMISSION_EXEC : 0));
1290 }
1291 }
1292 }
1293 # endif
1294 # ifdef RKTIO_SYSTEM_WINDOWS
1295 {
1296 int flags;
1297
1298 if (UNC_stat(rktio, filename, &flags, NULL, NULL, NULL, NULL, NULL, -1)) {
1299 if (all_bits)
1300 return (flags | (flags << 3) | (flags << 6));
1301 else
1302 return flags;
1303 } else {
1304 return RKTIO_PERMISSION_ERROR;
1305 }
1306 }
1307 # endif
1308 # endif
1309 }
1310
rktio_set_file_or_directory_permissions(rktio_t * rktio,const char * filename,int new_bits)1311 int rktio_set_file_or_directory_permissions(rktio_t *rktio, const char *filename, int new_bits)
1312 {
1313 # ifdef NO_STAT_PROC
1314 set_racket_error(RKTIO_ERROR_UNSUPPORTED);
1315 return -1;
1316 # else
1317 # ifdef RKTIO_SYSTEM_UNIX
1318 {
1319 int r;
1320
1321 do {
1322 r = chmod(filename, new_bits);
1323 } while ((r == -1) && (errno == EINTR));
1324 if (r) {
1325 get_posix_error();
1326 return 0;
1327 } else
1328 return 1;
1329 }
1330 # endif
1331 # ifdef RKTIO_SYSTEM_WINDOWS
1332 {
1333 int ALWAYS_SET_BITS = ((RKTIO_UNC_READ | RKTIO_UNC_EXEC)
1334 | ((RKTIO_UNC_READ | RKTIO_UNC_EXEC) << 3)
1335 | ((RKTIO_UNC_READ | RKTIO_UNC_EXEC) << 6));
1336 if (((new_bits & ALWAYS_SET_BITS) != ALWAYS_SET_BITS)
1337 || ((new_bits & RKTIO_UNC_WRITE) != ((new_bits & (RKTIO_UNC_WRITE << 3)) >> 3))
1338 || ((new_bits & RKTIO_UNC_WRITE) != ((new_bits & (RKTIO_UNC_WRITE << 6)) >> 6))
1339 || (new_bits >= (1 << 9))) {
1340 set_racket_error(RKTIO_ERROR_BAD_PERMISSION);
1341 return 0;
1342 }
1343
1344 if (UNC_stat(rktio, filename, NULL, NULL, NULL, NULL, NULL, NULL, new_bits))
1345 return 1;
1346
1347 return 0;
1348 }
1349 # endif
1350 # endif
1351 }
1352
rktio_file_size(rktio_t * rktio,const char * filename)1353 rktio_filesize_t *rktio_file_size(rktio_t *rktio, const char *filename)
1354 {
1355 rktio_filesize_t *sz = NULL;
1356 #ifdef RKTIO_SYSTEM_WINDOWS
1357 {
1358 rktio_filesize_t sz_v;
1359 if (UNC_stat(rktio, filename, NULL, NULL, NULL, NULL, &sz_v, NULL, -1)) {
1360 sz = malloc(sizeof(rktio_filesize_t));
1361 *sz = sz_v;
1362 return sz;
1363 }
1364 return NULL;
1365 }
1366 #else
1367 {
1368 struct BIG_OFF_T_IZE(stat) buf;
1369
1370 while (1) {
1371 if (!BIG_OFF_T_IZE(stat)(MSC_WIDE_PATH_temp(filename), &buf))
1372 break;
1373 else if (errno != EINTR) {
1374 get_posix_error();
1375 return NULL;
1376 }
1377 }
1378
1379 if (S_ISDIR(buf.st_mode)) {
1380 set_racket_error(RKTIO_ERROR_IS_A_DIRECTORY);
1381 return NULL;
1382 }
1383
1384 sz = malloc(sizeof(rktio_filesize_t));
1385 *sz = buf.st_size;
1386
1387 return sz;
1388 }
1389 #endif
1390 }
1391
1392 /*========================================================================*/
1393 /* directory list */
1394 /*========================================================================*/
1395
1396 #ifdef RKTIO_SYSTEM_WINDOWS
1397
1398 struct rktio_directory_list_t {
1399 int first_ready;
1400 FF_HANDLE_TYPE hfile;
1401 FF_TYPE info;
1402 };
1403
rktio_directory_list_start(rktio_t * rktio,const char * filename)1404 rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, const char *filename)
1405 /* path must be normalized */
1406 {
1407 char *pattern;
1408 int len;
1409 FF_HANDLE_TYPE hfile;
1410 FF_TYPE info;
1411 rktio_directory_list_t *dl;
1412 const wchar_t *wp;
1413
1414 retry:
1415
1416 {
1417 int is_ssq = 0, is_unc = 0, d, nd;
1418 len = strlen(filename);
1419 pattern = malloc(len + 14);
1420
1421 if (IS_A_DOS_SEP(filename[0])
1422 && IS_A_DOS_SEP(filename[1])) {
1423 if (filename[2] == '?')
1424 is_ssq = 1;
1425 else
1426 is_unc = 1;
1427 }
1428
1429 if (is_ssq) {
1430 d = 0;
1431 nd = 0;
1432 } else {
1433 pattern[0] = '\\';
1434 pattern[1] = '\\';
1435 pattern[2] = '?';
1436 pattern[3] = '\\';
1437 if (is_unc) {
1438 pattern[4] = 'U';
1439 pattern[5] = 'N';
1440 pattern[6] = 'C';
1441 pattern[7] = '\\';
1442 d = 8;
1443 nd = 2;
1444 } else {
1445 d = 4;
1446 nd = 0;
1447 }
1448 }
1449 memcpy(pattern + d, filename + nd, len - nd);
1450 len += (d - nd);
1451 if (len && !IS_A_DOS_SEP(pattern[len - 1]))
1452 pattern[len++] = '\\';
1453 memcpy(pattern + len, "*.*", 4);
1454 }
1455
1456 wp = WIDE_PATH_temp(pattern);
1457 if (!wp) return NULL;
1458
1459 hfile = FIND_FIRST(wp, &info);
1460 if (FIND_FAILED(hfile)) {
1461 int err_val;
1462 err_val = GetLastError();
1463 if ((err_val == ERROR_DIRECTORY) && CreateSymbolicLinkProc) {
1464 /* check for symbolic link */
1465 const char *resolved;
1466 if (UNC_stat(rktio, filename, NULL, NULL, NULL, NULL, NULL, &resolved, -1)) {
1467 if (resolved) {
1468 filename = (char *)resolved;
1469 goto retry;
1470 }
1471 }
1472 }
1473 get_windows_error();
1474 return NULL;
1475 }
1476
1477 dl = malloc(sizeof(rktio_directory_list_t));
1478 memcpy(&dl->info, &info, sizeof(info));
1479 dl->hfile = hfile;
1480
1481 return dl;
1482 }
1483
rktio_directory_list_step(rktio_t * rktio,rktio_directory_list_t * dl)1484 char *rktio_directory_list_step(rktio_t *rktio, rktio_directory_list_t *dl)
1485 /* empty-strng result (don't deallocate) means done */
1486 {
1487 while (dl->first_ready || FIND_NEXT(dl->hfile, &dl->info)) {
1488 dl->first_ready = 0;
1489 if ((GET_FF_NAME(dl->info)[0] == '.')
1490 && (!GET_FF_NAME(dl->info)[1] || ((GET_FF_NAME(dl->info)[1] == '.')
1491 && !GET_FF_NAME(dl->info)[2]))) {
1492 /* skip . and .. */
1493 } else {
1494 return NARROW_PATH_copy(dl->info.cFileName);
1495 }
1496 }
1497
1498 rktio_directory_list_stop(rktio, dl);
1499
1500 return "";
1501 }
1502
rktio_directory_list_stop(rktio_t * rktio,rktio_directory_list_t * dl)1503 void rktio_directory_list_stop(rktio_t *rktio, rktio_directory_list_t *dl)
1504 {
1505 FIND_CLOSE(dl->hfile);
1506 free(dl);
1507 }
1508
1509 # elif !defined(NO_READDIR)
1510
1511 struct rktio_directory_list_t {
1512 DIR *dir;
1513 };
1514
rktio_directory_list_start(rktio_t * rktio,const char * filename)1515 rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, const char *filename)
1516 {
1517 rktio_directory_list_t *dl;
1518 DIR *dir;
1519
1520 dir = opendir(filename ? filename : ".");
1521 if (!dir) {
1522 get_posix_error();
1523 return NULL;
1524 }
1525
1526 dl = malloc(sizeof(rktio_directory_list_t));
1527 dl->dir = dir;
1528
1529 return dl;
1530 }
1531
rktio_directory_list_step(rktio_t * rktio,rktio_directory_list_t * dl)1532 char *rktio_directory_list_step(rktio_t *rktio, rktio_directory_list_t *dl)
1533 /* empty-strng result (don't deallocate) means done */
1534 {
1535 struct dirent *e;
1536
1537 while ((e = readdir(dl->dir))) {
1538 int nlen;
1539
1540 # ifdef HAVE_DIRENT_NAMLEN
1541 nlen = e->d_namlen;
1542 # elif HAVE_DIRENT_NAMELEN
1543 /* Case for QNX - which seems to define d_namelen instead */
1544 nlen = e->d_namelen;
1545 # else
1546 nlen = strlen(e->d_name);
1547 # endif
1548
1549 # if defined(RKTIO_SYSTEM_UNIX) || defined(RKTIO_SYSTEM_WINDOWS)
1550 if (nlen == 1 && e->d_name[0] == '.')
1551 continue;
1552 if (nlen == 2 && e->d_name[0] == '.' && e->d_name[1] == '.')
1553 continue;
1554 # endif
1555
1556 return rktio_strndup(e->d_name, nlen);
1557 }
1558
1559 rktio_directory_list_stop(rktio, dl);
1560
1561 return "";
1562 }
1563
rktio_directory_list_stop(rktio_t * rktio,rktio_directory_list_t * dl)1564 void rktio_directory_list_stop(rktio_t *rktio, rktio_directory_list_t *dl)
1565 {
1566 closedir(dl->dir);
1567 free(dl);
1568 }
1569
1570 #else
1571
rktio_directory_list_start(rktio_t * rktio,char * filename)1572 rktio_directory_list_t *rktio_directory_list_start(rktio_t *rktio, char *filename)
1573 {
1574 set_racket_error(RKTIO_ERROR_UNSUPPORTED);
1575 return NULL;
1576 }
1577
rktio_directory_list_step(rktio_t * rktio,rktio_directory_list_t * dl)1578 char *rktio_directory_list_step(rktio_t *rktio, rktio_directory_list_t *dl)
1579 {
1580 set_racket_error(RKTIO_ERROR_UNSUPPORTED);
1581 return NULL;
1582 }
1583
rktio_directory_list_stop(rktio_t * rktio,rktio_directory_list_t * dl)1584 void rktio_directory_list_stop(rktio_t *rktio, rktio_directory_list_t *dl)
1585 {
1586 }
1587
1588 #endif
1589
1590 /*========================================================================*/
1591 /* copy file */
1592 /*========================================================================*/
1593
1594 struct rktio_file_copy_t {
1595 int done;
1596 rktio_fd_t *src_fd, *dest_fd;
1597 #ifdef RKTIO_SYSTEM_UNIX
1598 intptr_t mode;
1599 #endif
1600 };
1601
rktio_copy_file_start(rktio_t * rktio,const char * dest,const char * src,int exists_ok)1602 rktio_file_copy_t *rktio_copy_file_start(rktio_t *rktio, const char *dest, const char *src, int exists_ok)
1603 {
1604 #ifdef RKTIO_SYSTEM_UNIX
1605 {
1606 # define COPY_BUFFER_SIZE 2048
1607 int ok;
1608 struct stat buf;
1609 rktio_fd_t *src_fd, *dest_fd;
1610
1611 src_fd = rktio_open(rktio, src, RKTIO_OPEN_READ);
1612 if (!src_fd) {
1613 rktio_set_last_error_step(rktio, RKTIO_COPY_STEP_OPEN_SRC);
1614 return NULL;
1615 }
1616
1617 do {
1618 ok = fstat(rktio_fd_system_fd(rktio, src_fd), &buf);
1619 } while ((ok == -1) && (errno == EINTR));
1620
1621 if (ok || S_ISDIR(buf.st_mode)) {
1622 if (ok)
1623 get_posix_error();
1624 else
1625 set_racket_error(RKTIO_ERROR_IS_A_DIRECTORY);
1626 rktio_set_last_error_step(rktio, RKTIO_COPY_STEP_READ_SRC_METADATA);
1627 rktio_close(rktio, src_fd);
1628 return NULL;
1629 }
1630
1631 dest_fd = rktio_open(rktio, dest, (RKTIO_OPEN_WRITE
1632 | (exists_ok ? RKTIO_OPEN_TRUNCATE : 0)));
1633 if (!dest_fd) {
1634 rktio_close(rktio, src_fd);
1635 rktio_set_last_error_step(rktio, RKTIO_COPY_STEP_OPEN_DEST);
1636 return NULL;
1637 }
1638
1639 {
1640 rktio_file_copy_t *fc;
1641
1642 fc = malloc(sizeof(rktio_file_copy_t));
1643
1644 fc->done = 0;
1645 fc->src_fd = src_fd;
1646 fc->dest_fd = dest_fd;
1647 fc->mode = buf.st_mode;
1648
1649 return fc;
1650 }
1651 }
1652 #endif
1653 #ifdef RKTIO_SYSTEM_WINDOWS
1654 int err_val = 0;
1655 wchar_t *src_w;
1656 const wchar_t *dest_w;
1657
1658 src_w = WIDE_PATH_copy(src);
1659 if (!src_w) {
1660 rktio_set_last_error_step(rktio, RKTIO_COPY_STEP_OPEN_SRC);
1661 return NULL;
1662 }
1663 dest_w = WIDE_PATH_temp(dest);
1664 if (!dest_w) {
1665 rktio_set_last_error_step(rktio, RKTIO_COPY_STEP_OPEN_DEST);
1666 return NULL;
1667 }
1668
1669 if (CopyFileW(src_w, dest_w, !exists_ok)) {
1670 rktio_file_copy_t *fc;
1671 free(src_w);
1672 /* Return a pointer to indicate success: */
1673 fc = malloc(sizeof(rktio_file_copy_t));
1674 fc->done = 1;
1675 return fc;
1676 }
1677
1678 err_val = GetLastError();
1679 if ((err_val == ERROR_FILE_EXISTS)
1680 || (err_val == ERROR_ALREADY_EXISTS))
1681 set_racket_error(RKTIO_ERROR_EXISTS);
1682 else
1683 get_windows_error();
1684 rktio_set_last_error_step(rktio, RKTIO_COPY_STEP_UNKNOWN);
1685
1686 free(src_w);
1687
1688 return NULL;
1689 #endif
1690 }
1691
rktio_copy_file_is_done(rktio_t * rktio,rktio_file_copy_t * fc)1692 rktio_bool_t rktio_copy_file_is_done(rktio_t *rktio, rktio_file_copy_t *fc)
1693 {
1694 return fc->done;
1695 }
1696
rktio_copy_file_step(rktio_t * rktio,rktio_file_copy_t * fc)1697 rktio_ok_t rktio_copy_file_step(rktio_t *rktio, rktio_file_copy_t *fc)
1698 {
1699 #ifdef RKTIO_SYSTEM_UNIX
1700 char buffer[4096];
1701 intptr_t len;
1702
1703 if (fc->done)
1704 return 1;
1705
1706 len = rktio_read(rktio, fc->src_fd, buffer, sizeof(buffer));
1707 if (len == RKTIO_READ_EOF) {
1708 fc->done = 1;
1709 return 1;
1710 } else if (len == RKTIO_READ_ERROR) {
1711 rktio_set_last_error_step(rktio, RKTIO_COPY_STEP_READ_SRC_DATA);
1712 return 0;
1713 } else {
1714 intptr_t done = 0, amt;
1715
1716 while (done < len) {
1717 amt = rktio_write(rktio, fc->dest_fd, buffer + done, len - done);
1718 if (amt < 0) {
1719 rktio_set_last_error_step(rktio, RKTIO_COPY_STEP_WRITE_DEST_DATA);
1720 return 0;
1721 }
1722 done += amt;
1723 }
1724 return 1;
1725 }
1726 #endif
1727 #ifdef RKTIO_SYSTEM_WINDOWS
1728 return 1;
1729 #endif
1730 }
1731
rktio_copy_file_finish_permissions(rktio_t * rktio,rktio_file_copy_t * fc)1732 rktio_ok_t rktio_copy_file_finish_permissions(rktio_t *rktio, rktio_file_copy_t *fc)
1733 {
1734 #ifdef RKTIO_SYSTEM_UNIX
1735 int err;
1736
1737 do {
1738 err = fchmod(rktio_fd_system_fd(rktio, fc->dest_fd), fc->mode);
1739 } while ((err == -1) && (errno != EINTR));
1740
1741 if (err) {
1742 get_posix_error();
1743 rktio_set_last_error_step(rktio, RKTIO_COPY_STEP_WRITE_DEST_METADATA);
1744 return 0;
1745 }
1746 #endif
1747 return 1;
1748 }
1749
rktio_copy_file_stop(rktio_t * rktio,rktio_file_copy_t * fc)1750 void rktio_copy_file_stop(rktio_t *rktio, rktio_file_copy_t *fc)
1751 {
1752 #ifdef RKTIO_SYSTEM_UNIX
1753 rktio_close(rktio, fc->src_fd);
1754 rktio_close(rktio, fc->dest_fd);
1755 #endif
1756 free(fc);
1757 }
1758
1759 /*========================================================================*/
1760 /* filesystem root list */
1761 /*========================================================================*/
1762
rktio_filesystem_roots(rktio_t * rktio)1763 char **rktio_filesystem_roots(rktio_t *rktio)
1764 /* returns a NULL-terminated array of strings */
1765 {
1766 #ifdef RKTIO_SYSTEM_WINDOWS
1767 {
1768 # define DRIVE_BUF_SIZE 1024
1769 char drives[DRIVE_BUF_SIZE], *s;
1770 intptr_t len, ds, ss_len, ss_count = 0;
1771 UINT oldmode;
1772 char **ss, **new_ss;
1773
1774 len = GetLogicalDriveStringsA(DRIVE_BUF_SIZE, drives);
1775 if (len <= DRIVE_BUF_SIZE)
1776 s = drives;
1777 else {
1778 s = malloc(len + 1);
1779 GetLogicalDriveStringsA(len + 1, s);
1780 }
1781
1782 ss_len = 8;
1783 ss = malloc(sizeof(char*) * ss_len);
1784
1785 ds = 0;
1786 oldmode = SetErrorMode(SEM_FAILCRITICALERRORS);
1787 while (s[ds]) {
1788 DWORD a, b, c, d;
1789 /* GetDiskFreeSpace effectively checks whether we can read the disk: */
1790 if (GetDiskFreeSpaceA(s + ds, &a, &b, &c, &d)) {
1791 if ((ss_count + 1) == ss_len) {
1792 new_ss = malloc(sizeof(char*) * ss_len * 2);
1793 memcpy(ss, new_ss, ss_count * sizeof(char*));
1794 ss = new_ss;
1795 ss_len *= 2;
1796 }
1797
1798 ss[ss_count++] = MSC_IZE(strdup)(s + ds);
1799 }
1800 ds += strlen(s + ds) + 1;
1801 }
1802 SetErrorMode(oldmode);
1803
1804 if (s != drives)
1805 free(s);
1806
1807 ss[ss_count] = 0;
1808
1809 return ss;
1810 }
1811 #else
1812 char **ss;
1813
1814 ss = malloc(sizeof(char*) * 2);
1815 ss[0] = strdup("/");
1816 ss[1] = NULL;
1817
1818 return ss;
1819 #endif
1820 }
1821
1822 /*========================================================================*/
1823 /* expand user tilde & system paths */
1824 /*========================================================================*/
1825
rktio_expand_user_tilde(rktio_t * rktio,const char * filename)1826 char *rktio_expand_user_tilde(rktio_t *rktio, const char *filename) {
1827 #ifdef RKTIO_SYSTEM_WINDOWS
1828 set_racket_error(RKTIO_ERROR_UNSUPPORTED);
1829 return NULL;
1830 #else
1831 char user[256], *home = NULL, *naya;
1832 struct passwd *who = NULL;
1833 intptr_t u, f, len, flen, ilen;
1834
1835 if (filename[0] != '~') {
1836 set_racket_error(RKTIO_ERROR_NO_TILDE);
1837 return NULL;
1838 }
1839
1840 for (u = 0, f = 1;
1841 u < 255 && filename[f] && filename[f] != '/';
1842 u++, f++) {
1843 user[u] = filename[f];
1844 }
1845
1846 if (filename[f] && filename[f] != '/') {
1847 set_racket_error(RKTIO_ERROR_ILL_FORMED_USER);
1848 return NULL;
1849 }
1850 user[u] = 0;
1851
1852 if (!user[0]) {
1853 if (!(home = rktio_getenv(rktio, "HOME"))) {
1854 char *ptr;
1855
1856 ptr = rktio_getenv(rktio, "USER");
1857 if (!ptr)
1858 ptr = rktio_getenv(rktio, "LOGNAME");
1859
1860 who = ptr ? getpwnam(ptr) : NULL;
1861
1862 if (ptr) free(ptr);
1863
1864 if (!who)
1865 who = getpwuid(getuid());
1866 }
1867 } else
1868 who = getpwnam(user);
1869
1870 if (!home && who && who->pw_dir)
1871 home = strdup(who->pw_dir);
1872
1873 if (!home) {
1874 set_racket_error(RKTIO_ERROR_UNKNOWN_USER);
1875 return NULL;
1876 }
1877
1878 ilen = strlen(filename);
1879 len = strlen(home);
1880 if (f < ilen)
1881 flen = ilen - f - 1;
1882 else
1883 flen = 0;
1884 naya = (char *)malloc(len + flen + 2);
1885 memcpy(naya, home, len);
1886 naya[len] = '/';
1887 memcpy(naya + len + 1, filename + f + 1, flen);
1888 naya[len + flen + 1] = 0;
1889
1890 free(home);
1891
1892 return naya;
1893 #endif
1894 }
1895
append_paths(char * a,char * b,int free_a,int free_b)1896 static char *append_paths(char *a, char *b, int free_a, int free_b)
1897 {
1898 int alen = strlen(a);
1899 int blen = strlen(b);
1900 int sep_len = 0;
1901 char *s;
1902
1903 if (alen && !IS_A_SEP(a[alen-1]))
1904 sep_len = 1;
1905
1906 s = malloc(alen + sep_len + blen + 1);
1907
1908 memcpy(s, a, alen);
1909 if (sep_len)
1910 s[alen] = A_PATH_SEP;
1911 memcpy(s+alen+sep_len, b, blen);
1912 s[alen + sep_len + blen] = 0;
1913
1914 if (free_a) free(a);
1915 if (free_b) free(b);
1916
1917 return s;
1918 }
1919
1920 #ifdef RKTIO_SYSTEM_UNIX
directory_or_file_exists(rktio_t * rktio,char * dir,char * maybe_file)1921 static int directory_or_file_exists(rktio_t *rktio, char *dir, char *maybe_file)
1922 {
1923 if (maybe_file) {
1924 int r;
1925 char *path = append_paths(dir, maybe_file, 0, 0);
1926 r = rktio_file_exists(rktio, path);
1927 free(path);
1928 return r;
1929 } else
1930 return rktio_directory_exists(rktio, dir);
1931 }
1932 #endif
1933
rktio_system_path(rktio_t * rktio,int which)1934 char *rktio_system_path(rktio_t *rktio, int which)
1935 {
1936 #ifdef RKTIO_SYSTEM_UNIX
1937 if (which == RKTIO_PATH_SYS_DIR) {
1938 return strdup("/");
1939 }
1940
1941 if (which == RKTIO_PATH_TEMP_DIR) {
1942 char *p;
1943
1944 if ((p = rktio_getenv(rktio, "TMPDIR"))) {
1945 if (rktio_directory_exists(rktio, p))
1946 return p;
1947 else
1948 free(p);
1949 }
1950
1951 if (rktio_directory_exists(rktio, "/var/tmp"))
1952 return strdup("/var/tmp");
1953
1954 if (rktio_directory_exists(rktio, "/usr/tmp"))
1955 return strdup("/usr/tmp");
1956
1957 if (rktio_directory_exists(rktio, "/tmp"))
1958 return strdup("/tmp");
1959
1960 return rktio_get_current_directory(rktio);
1961 }
1962
1963 {
1964 /* Everything else is in ~: */
1965 char *home_str, *alt_home, *home, *default_xdg_home_str = NULL, *xdg_home_str = NULL, *prefer_home;
1966 char *home_file = NULL, *prefer_home_file = NULL;
1967
1968 alt_home = rktio_getenv(rktio, "PLTUSERHOME");
1969
1970 if ((which == RKTIO_PATH_PREF_DIR)
1971 || (which == RKTIO_PATH_PREF_FILE)
1972 || (which == RKTIO_PATH_ADDON_DIR)
1973 || (which == RKTIO_PATH_CACHE_DIR)
1974 || (which == RKTIO_PATH_INIT_DIR)
1975 || (which == RKTIO_PATH_INIT_FILE)) {
1976 #if defined(OS_X) && !defined(RACKET_XONX) && !defined(XONX)
1977 if (which == RKTIO_PATH_ADDON_DIR)
1978 home_str = "~/Library/Racket/";
1979 else if (which == RKTIO_PATH_CACHE_DIR)
1980 home_str = "~/Library/Caches/Racket/";
1981 else if ((which == RKTIO_PATH_INIT_DIR)
1982 || (which == RKTIO_PATH_INIT_FILE)) {
1983 default_xdg_home_str = "~/Library/Racket/";
1984 prefer_home_file = "racketrc.rktl";
1985 home_str = "~/";
1986 home_file = ".racketrc";
1987 } else
1988 home_str = "~/Library/Preferences/";
1989 #else
1990 char *envvar, *xdg_dir;
1991 if (which == RKTIO_PATH_ADDON_DIR) {
1992 default_xdg_home_str = "~/.local/share/racket/";
1993 envvar = "XDG_DATA_HOME";
1994 } else if (which == RKTIO_PATH_CACHE_DIR) {
1995 default_xdg_home_str = "~/.cache/racket/";
1996 envvar = "XDG_CACHE_HOME";
1997 } else {
1998 default_xdg_home_str = "~/.config/racket/";
1999 envvar = "XDG_CONFIG_HOME";
2000 }
2001 if (alt_home)
2002 xdg_dir = NULL;
2003 else
2004 xdg_dir = rktio_getenv(rktio, envvar);
2005 /* xdg_dir is invalid if it is not an absolute path */
2006 if (xdg_dir && (strlen(xdg_dir) > 0) && (xdg_dir[0] == '/')) {
2007 xdg_home_str = append_paths(xdg_dir, "racket/", 1, 0);
2008 } else {
2009 if (xdg_dir) free(xdg_dir);
2010 }
2011
2012 if ((which == RKTIO_PATH_INIT_DIR) || (which == RKTIO_PATH_INIT_FILE)) {
2013 home_str = "~/";
2014 home_file = ".racketrc";
2015 } else { /* RKTIO_PATH_{ADDON_DIR,PREF_DIR,PREF_FILE,CACHE_DIR} */
2016 home_str = "~/.racket/";
2017 }
2018 #endif
2019 } else {
2020 #if defined(OS_X) && !defined(RACKET_XONX) && !defined(XONX)
2021 if (which == RKTIO_PATH_DESK_DIR)
2022 home_str = "~/Desktop/";
2023 else if (which == RKTIO_PATH_DOC_DIR)
2024 home_str = "~/Documents/";
2025 else
2026 #endif
2027 home_str = "~/";
2028 }
2029
2030 /* If `xdg_home_str` is non-NULL, it must be an absolute
2031 path that is `malloc`ed.
2032 If `default_xdg_home_str` is non-NULL, it starts with "~/"
2033 and is not `malloc`ed. */
2034
2035 if (xdg_home_str || default_xdg_home_str) {
2036 if (xdg_home_str)
2037 prefer_home = xdg_home_str;
2038 else if (alt_home)
2039 prefer_home = append_paths(alt_home, default_xdg_home_str + 2, 0, 0);
2040 else
2041 prefer_home = rktio_expand_user_tilde(rktio, default_xdg_home_str);
2042
2043 if (directory_or_file_exists(rktio, prefer_home, prefer_home_file))
2044 home_str = NULL;
2045 } else
2046 prefer_home = NULL;
2047
2048 if (home_str) {
2049 if (alt_home)
2050 home = append_paths(alt_home, home_str + 2, 1, 0);
2051 else
2052 home = rktio_expand_user_tilde(rktio, home_str);
2053
2054 if (prefer_home) {
2055 if (!directory_or_file_exists(rktio, home, home_file)) {
2056 free(home);
2057 home = prefer_home;
2058 } else {
2059 free(prefer_home);
2060 prefer_home = NULL;
2061 }
2062 }
2063 } else
2064 home = prefer_home;
2065
2066 /* At this point, we're using `home`, but `prefer_home` can still
2067 be non-NULL and equal to `home` to mean that we should use
2068 XDG-style file names. */
2069
2070 if ((which == RKTIO_PATH_PREF_DIR) || (which == RKTIO_PATH_INIT_DIR)
2071 || (which == RKTIO_PATH_HOME_DIR) || (which == RKTIO_PATH_ADDON_DIR)
2072 || (which == RKTIO_PATH_DESK_DIR) || (which == RKTIO_PATH_DOC_DIR)
2073 || (which == RKTIO_PATH_CACHE_DIR))
2074 return home;
2075
2076 if (which == RKTIO_PATH_INIT_FILE) {
2077 if (prefer_home)
2078 return append_paths(prefer_home, "racketrc.rktl", 1, 0);
2079 else
2080 return append_paths(home, ".racketrc", 1, 0);
2081 }
2082
2083 if (which == RKTIO_PATH_PREF_FILE) {
2084 #if defined(OS_X) && !defined(RACKET_XONX) && !defined(XONX)
2085 return append_paths(home, "org.racket-lang.prefs.rktd", 1, 0);
2086 #else
2087 return append_paths(home, "racket-prefs.rktd", 1, 0);
2088 #endif
2089 } else {
2090 free(home);
2091 return strdup("/");
2092 }
2093 }
2094 #endif
2095
2096 #ifdef RKTIO_SYSTEM_WINDOWS
2097 if (which == RKTIO_PATH_SYS_DIR) {
2098 int size;
2099 wchar_t *s;
2100 size = GetSystemDirectoryW(NULL, 0);
2101 s = (wchar_t *)malloc((size + 1) * sizeof(wchar_t));
2102 GetSystemDirectoryW(s, size + 1);
2103 return NARROW_PATH_copy_then_free(s);
2104 }
2105
2106 {
2107 char *d, *p;
2108 char *home;
2109
2110 if (which == RKTIO_PATH_TEMP_DIR) {
2111 if ((p = rktio_getenv(rktio, "TMP")) || (p = rktio_getenv(rktio, "TEMP"))) {
2112 if (p) {
2113 if (rktio_directory_exists(rktio, p))
2114 return p;
2115 free(p);
2116 }
2117 }
2118
2119 return rktio_get_current_directory(rktio);
2120 }
2121
2122 home = NULL;
2123
2124 p = rktio_getenv(rktio, "PLTUSERHOME");
2125 if (p)
2126 home = p;
2127
2128 if (!home) {
2129 /* Try to get Application Data directory: */
2130 LPITEMIDLIST items;
2131 int which_folder;
2132
2133 if ((which == RKTIO_PATH_ADDON_DIR)
2134 || (which == RKTIO_PATH_CACHE_DIR) /* maybe CSIDL_LOCAL_APPDATA instead? */
2135 || (which == RKTIO_PATH_PREF_DIR)
2136 || (which == RKTIO_PATH_PREF_FILE))
2137 which_folder = CSIDL_APPDATA;
2138 else if (which == RKTIO_PATH_DOC_DIR) {
2139 # ifndef CSIDL_PERSONAL
2140 # define CSIDL_PERSONAL 0x0005
2141 # endif
2142 which_folder = CSIDL_PERSONAL;
2143 } else if (which == RKTIO_PATH_DESK_DIR)
2144 which_folder = CSIDL_DESKTOPDIRECTORY;
2145 else {
2146 # ifndef CSIDL_PROFILE
2147 # define CSIDL_PROFILE 0x0028
2148 # endif
2149 which_folder = CSIDL_PROFILE;
2150 }
2151
2152 if (SHGetSpecialFolderLocation(NULL, which_folder, &items) == S_OK) {
2153 int ok;
2154 IMalloc *mi;
2155 wchar_t *buf;
2156
2157 buf = (wchar_t *)malloc(MAX_PATH * sizeof(wchar_t));
2158 ok = SHGetPathFromIDListW(items, buf);
2159
2160 SHGetMalloc(&mi);
2161 mi->lpVtbl->Free(mi, items);
2162 mi->lpVtbl->Release(mi);
2163
2164 if (ok)
2165 home = NARROW_PATH_copy_then_free(buf);
2166 }
2167 }
2168
2169 if (!home) {
2170 /* Back-up: try USERPROFILE environment variable */
2171 d = rktio_getenv(rktio, "USERPROFILE");
2172 if (d) {
2173 if (rktio_directory_exists(rktio, d))
2174 return d;
2175 free(d);
2176 }
2177 }
2178
2179 if (!home) {
2180 /* Last-ditch effort: try HOMEDRIVE+HOMEPATH */
2181 d = rktio_getenv(rktio, "HOMEDRIVE");
2182 p = rktio_getenv(rktio, "HOMEPATH");
2183
2184 if (d && p) {
2185 home = append_paths(d, p, 1, 1);
2186
2187 if (rktio_directory_exists(rktio, home))
2188 return home;
2189 else {
2190 free(home);
2191 home = NULL;
2192 }
2193 } else
2194 home = NULL;
2195
2196 if (!home) {
2197 wchar_t name[1024];
2198
2199 if (!GetModuleFileNameW(NULL, name, 1024)) {
2200 /* Disaster. Use CWD. */
2201 home = rktio_get_current_directory(rktio);
2202 if (!home)
2203 return NULL;
2204 } else {
2205 int i;
2206 wchar_t *s;
2207
2208 s = name;
2209
2210 i = wcslen(s) - 1;
2211
2212 while (i && (s[i] != '\\')) {
2213 --i;
2214 }
2215 s[i] = 0;
2216 home = NARROW_PATH_copy(s);
2217 }
2218 }
2219 }
2220
2221 if ((which == RKTIO_PATH_INIT_DIR)
2222 || (which == RKTIO_PATH_HOME_DIR)
2223 || (which == RKTIO_PATH_DOC_DIR)
2224 || (which == RKTIO_PATH_DESK_DIR))
2225 return home;
2226
2227 if ((which == RKTIO_PATH_ADDON_DIR)
2228 || (which == RKTIO_PATH_CACHE_DIR)
2229 || (which == RKTIO_PATH_PREF_DIR)
2230 || (which == RKTIO_PATH_PREF_FILE)) {
2231 home = append_paths(home, "Racket", 1, 0);
2232 }
2233
2234 if (which == RKTIO_PATH_INIT_FILE)
2235 return append_paths(home, "racketrc.rktl", 1, 0);
2236 if (which == RKTIO_PATH_PREF_FILE)
2237 return append_paths(home, "racket-prefs.rktd", 1, 0);
2238 return home;
2239 }
2240 #endif
2241 }
2242
2243 /*========================================================================*/
2244 /* system information as a string */
2245 /*========================================================================*/
2246
2247
2248 #ifdef RKTIO_SYSTEM_UNIX
rktio_uname(rktio_t * rktio)2249 char *rktio_uname(rktio_t *rktio) {
2250 char *s;
2251 struct utsname u;
2252 int ok, len;
2253 int syslen, nodelen, rellen, verlen, machlen;
2254
2255 do {
2256 ok = uname(&u);
2257 } while ((ok == -1) && (errno == EINTR));
2258
2259 if (ok != 0)
2260 return strdup("<unknown machine>");
2261
2262 syslen = strlen(u.sysname);
2263 nodelen = strlen(u.nodename);
2264 rellen = strlen(u.release);
2265 verlen = strlen(u.version);
2266 machlen = strlen(u.machine);
2267
2268 len = (syslen + 1 + nodelen + 1 + rellen + 1 + verlen + 1 + machlen + 1);
2269
2270 s = malloc(len);
2271
2272 # define ADD_UNAME_STR(sn, slen) do { \
2273 memcpy(s + len, sn, slen); \
2274 len += slen; \
2275 s[len++] = ' '; \
2276 } while (0)
2277
2278 len = 0;
2279 ADD_UNAME_STR(u.sysname, syslen);
2280 ADD_UNAME_STR(u.nodename, nodelen);
2281 ADD_UNAME_STR(u.release, rellen);
2282 ADD_UNAME_STR(u.version, verlen);
2283 ADD_UNAME_STR(u.machine, machlen);
2284 s[len - 1] = 0;
2285
2286 # undef ADD_UNAME_STR
2287
2288 return s;
2289 }
2290 #endif
2291
2292 #ifdef RKTIO_SYSTEM_WINDOWS
rktio_uname(rktio_t * rktio)2293 char *rktio_uname(rktio_t *rktio) {
2294 char buff[1024];
2295 OSVERSIONINFO info;
2296 BOOL hasInfo;
2297 char *p;
2298
2299 info.dwOSVersionInfoSize = sizeof(info);
2300
2301 GetVersionEx(&info);
2302
2303 hasInfo = FALSE;
2304
2305 p = info.szCSDVersion;
2306
2307 while (p < info.szCSDVersion + sizeof(info.szCSDVersion) &&
2308 *p) {
2309 if (*p != ' ') {
2310 hasInfo = TRUE;
2311 break;
2312 }
2313 p = p + 1;
2314 }
2315
2316 sprintf(buff,"Windows %s %ld.%ld (Build %ld)%s%s",
2317 (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) ?
2318 "9x" :
2319 (info.dwPlatformId == VER_PLATFORM_WIN32_NT) ?
2320 "NT" : "Unknown platform",
2321 info.dwMajorVersion,info.dwMinorVersion,
2322 (info.dwPlatformId == VER_PLATFORM_WIN32_NT) ?
2323 info.dwBuildNumber :
2324 info.dwBuildNumber & 0xFFFF,
2325 hasInfo ? " " : "",hasInfo ? info.szCSDVersion : "");
2326
2327 return strdup(buff);
2328 }
2329 #endif
2330