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