1 /* -*-C-*-
2 
3 Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
4     1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
5     2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Massachusetts
6     Institute of Technology
7 
8 This file is part of MIT/GNU Scheme.
9 
10 MIT/GNU Scheme is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or (at
13 your option) any later version.
14 
15 MIT/GNU Scheme is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with MIT/GNU Scheme; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301,
23 USA.
24 
25 */
26 
27 #include "nt.h"
28 #include "ntfs.h"
29 #include <string.h>
30 #include "outf.h"
31 
32 #ifndef FILE_TOUCH_OPEN_TRIES
33 #  define FILE_TOUCH_OPEN_TRIES 5
34 #endif
35 
36 static enum get_file_info_result get_file_info_from_dir
37   (const char *, BY_HANDLE_FILE_INFORMATION *, int);
38 static int valid_drive_p (const char *);
39 static HANDLE create_file_for_info (const char *);
40 
41 enum get_file_info_result
NT_get_file_info(const char * namestring,BY_HANDLE_FILE_INFORMATION * info,int inaccessible_ok)42 NT_get_file_info (const char * namestring, BY_HANDLE_FILE_INFORMATION * info,
43 		  int inaccessible_ok)
44 {
45   char nscopy [MAX_PATH];
46   HANDLE hfile;
47 
48   strcpy (nscopy, namestring);
49   {
50     unsigned int len = (strlen (nscopy));
51     if ((len > 3) && ((nscopy [len - 1]) == '\\'))
52       (nscopy [len - 1]) = '\0';
53   }
54   hfile = (create_file_for_info (nscopy));
55   if (hfile == INVALID_HANDLE_VALUE)
56     {
57       DWORD code = (GetLastError ());
58       if (STAT_NOT_FOUND_P (code))
59 	return (gfi_not_found);
60       if (STAT_NOT_ACCESSIBLE_P (code))
61 	return (get_file_info_from_dir (nscopy, info, inaccessible_ok));
62       NT_error_api_call (code, apicall_CreateFile);
63     }
64   if (!GetFileInformationByHandle (hfile, info))
65     {
66       DWORD code = (GetLastError ());
67       (void) CloseHandle (hfile);
68       if (STAT_NOT_FOUND_P (code))
69 	return (gfi_not_found);
70       if (inaccessible_ok && (STAT_NOT_ACCESSIBLE_P (code)))
71 	return (gfi_not_accessible);
72       NT_error_api_call (code, apicall_GetFileInformationByHandle);
73     }
74   STD_BOOL_API_CALL (CloseHandle, (hfile));
75   return (gfi_ok);
76 }
77 
78 /* Incredible kludge.  Some files (e.g. \pagefile.sys) cannot be
79    accessed by the usual technique, but much of the same information
80    is available by reading the directory.  More M$ bullshit.  */
81 static enum get_file_info_result
get_file_info_from_dir(const char * namestring,BY_HANDLE_FILE_INFORMATION * info,int inaccessible_ok)82 get_file_info_from_dir (const char * namestring,
83 			BY_HANDLE_FILE_INFORMATION * info,
84 			int inaccessible_ok)
85 {
86   WIN32_FIND_DATA fi;
87   HANDLE handle = (FindFirstFile (namestring, (&fi)));
88   if (handle == INVALID_HANDLE_VALUE)
89     {
90       DWORD code = (GetLastError ());
91       if (STAT_NOT_FOUND_P (code))
92 	{
93 	  /* On Windows 95, the root directory of a drive cannot be
94 	     interrogated using either method.  Test to see if it is a
95 	     valid drive name, and if so, dummy it.  */
96 	  if (((strlen (namestring)) == 3)
97 	      && ((namestring[1]) == ':')
98 	      && ((namestring[2]) == '\\')
99 	      && (valid_drive_p (namestring)))
100 	    {
101 	      (info -> dwFileAttributes) = FILE_ATTRIBUTE_DIRECTORY;
102 	      ((info -> ftCreationTime) . dwHighDateTime) = 0;
103 	      ((info -> ftCreationTime) . dwLowDateTime) = 0;
104 	      ((info -> ftLastAccessTime) . dwHighDateTime) = 0;
105 	      ((info -> ftLastAccessTime) . dwLowDateTime) = 0;
106 	      ((info -> ftLastWriteTime) . dwHighDateTime) = 0;
107 	      ((info -> ftLastWriteTime) . dwLowDateTime) = 0;
108 	      (info -> dwVolumeSerialNumber) = 0;
109 	      (info -> nFileSizeHigh) = 0;
110 	      (info -> nFileSizeLow) = 0;
111 	      (info -> nNumberOfLinks) = 1;
112 	      (info -> nFileIndexHigh) = 0;
113 	      (info -> nFileIndexLow) = 0;
114 	      return (gfi_ok);
115 	    }
116 	  else
117 	    return (gfi_not_found);
118 	}
119       if (inaccessible_ok && (STAT_NOT_ACCESSIBLE_P (code)))
120 	return (gfi_not_accessible);
121       NT_error_api_call (code, apicall_FindFirstFile);
122     }
123   FindClose (handle);
124   (info -> dwFileAttributes) = (fi . dwFileAttributes);
125   (info -> ftCreationTime) = (fi . ftCreationTime);
126   (info -> ftLastAccessTime) = (fi . ftLastAccessTime);
127   (info -> ftLastWriteTime) = (fi . ftLastWriteTime);
128   (info -> dwVolumeSerialNumber) = 0;
129   (info -> nFileSizeHigh) = (fi . nFileSizeHigh);
130   (info -> nFileSizeLow) = (fi . nFileSizeLow);
131   (info -> nNumberOfLinks) = 1;
132   (info -> nFileIndexHigh) = 0;
133   (info -> nFileIndexLow) = 0;
134   return (gfi_ok);
135 }
136 
137 static int
valid_drive_p(const char * namestring)138 valid_drive_p (const char * namestring)
139 {
140   DWORD sectors_per_cluster;
141   DWORD bytes_per_sector;
142   DWORD number_of_free_clusters;
143   DWORD total_number_of_clusters;
144   return
145     (GetDiskFreeSpace (namestring,
146 		       (&sectors_per_cluster),
147 		       (&bytes_per_sector),
148 		       (&number_of_free_clusters),
149 		       (&total_number_of_clusters)));
150 }
151 
152 static HANDLE
create_file_for_info(const char * namestring)153 create_file_for_info (const char * namestring)
154 {
155   return
156     (CreateFile (namestring,
157 		 0,
158 		 (FILE_SHARE_READ | FILE_SHARE_WRITE),
159 		 0,
160 		 OPEN_EXISTING,
161 		 FILE_FLAG_BACKUP_SEMANTICS,
162 		 NULL));
163 }
164 
165 enum file_existence
OS_file_existence_test(const char * name)166 OS_file_existence_test (const char * name)
167 {
168   BY_HANDLE_FILE_INFORMATION info;
169   return
170     (((NT_get_file_info (name, (&info), 1)) == gfi_ok)
171      ? file_does_exist
172      : file_doesnt_exist);
173 }
174 
175 enum file_existence
OS_file_existence_test_direct(const char * name)176 OS_file_existence_test_direct (const char * name)
177 {
178   return (OS_file_existence_test (name));
179 }
180 
181 enum file_type
OS_file_type_direct(const char * name)182 OS_file_type_direct (const char * name)
183 {
184   BY_HANDLE_FILE_INFORMATION info;
185   return
186     (((NT_get_file_info (name, (&info), 0)) == gfi_not_found)
187      ? file_type_nonexistent
188      : (((info . dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) == 0)
189      ? file_type_regular
190      : file_type_directory);
191 }
192 
193 enum file_type
OS_file_type_indirect(const char * name)194 OS_file_type_indirect (const char * name)
195 {
196   return (OS_file_type_direct (name));
197 }
198 
199 #define R_OK 4
200 #define W_OK 2
201 #define X_OK 1
202 
203 int
OS_file_access(const char * name,unsigned int mode)204 OS_file_access (const char * name, unsigned int mode)
205 {
206   BY_HANDLE_FILE_INFORMATION info;
207   if ((NT_get_file_info (name, (&info), 1)) != gfi_ok)
208     return (0);
209   if (((mode & W_OK) != 0)
210       && (((info . dwFileAttributes) & FILE_ATTRIBUTE_READONLY) != 0))
211     return (0);
212   if (((mode & X_OK) != 0)
213       && (((info . dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) == 0))
214     {
215       const char * extension = (strrchr (name, '.'));
216       if (! (((stricmp (extension, ".exe")) == 0)
217 	     || ((stricmp (extension, ".com")) == 0)
218 	     || ((stricmp (extension, ".bat")) == 0)))
219 	return (0);
220     }
221   return (1);
222 }
223 
224 int
OS_file_directory_p(const char * name)225 OS_file_directory_p (const char * name)
226 {
227   BY_HANDLE_FILE_INFORMATION info;
228   return
229     (((NT_get_file_info (name, (&info), 0)) == gfi_ok)
230      && (((info . dwFileAttributes) & FILE_ATTRIBUTE_DIRECTORY) != 0));
231 }
232 
233 const char *
OS_file_soft_link_p(const char * name)234 OS_file_soft_link_p (const char * name)
235 {
236   return (0);
237 }
238 
239 static void
guarantee_writable(const char * name,int errorp)240 guarantee_writable (const char * name,
241        int errorp)
242 {
243   DWORD attributes = (GetFileAttributes (name));
244   if (attributes == 0xFFFFFFFF)
245     {
246       DWORD error_code = (GetLastError ());
247       if ((! ((error_code == ERROR_FILE_NOT_FOUND)
248 	      || (error_code == ERROR_PATH_NOT_FOUND)))
249 	  && errorp)
250 	NT_error_api_call (error_code, apicall_GetFileAttributes);
251     }
252   else if ((attributes & FILE_ATTRIBUTE_READONLY) != 0)
253     {
254       if ((! (SetFileAttributes (name,
255 				 (attributes &~ FILE_ATTRIBUTE_READONLY))))
256 	  && errorp)
257 	NT_error_api_call ((GetLastError ()), apicall_SetFileAttributes);
258     }
259 }
260 
261 void
OS_file_remove(const char * name)262 OS_file_remove (const char * name)
263 {
264   guarantee_writable (name, 1);
265   STD_BOOL_API_CALL (DeleteFile, (name));
266 }
267 
268 void
OS_file_remove_link(const char * name)269 OS_file_remove_link (const char * name)
270 {
271   struct stat s;
272   if ((stat (name, (&s)) == 0)
273       && (((s . st_mode) & S_IFMT) == S_IFREG))
274     {
275       guarantee_writable (name, 0);
276       unlink (name);
277     }
278 }
279 
280 void
OS_file_rename(const char * from,const char * to)281 OS_file_rename (const char * from, const char * to)
282 {
283   guarantee_writable (to, 1);
284   STD_BOOL_API_CALL (MoveFile, (from, to));
285 }
286 
287 void
OS_file_copy(const char * from,const char * to)288 OS_file_copy (const char * from, const char * to)
289 {
290   guarantee_writable (to, 1);
291   STD_BOOL_API_CALL (CopyFile, (from, to, FALSE));
292 }
293 
294 void
OS_file_link_hard(const char * from_name,const char * to_name)295 OS_file_link_hard (const char * from_name, const char * to_name)
296 {
297   error_unimplemented_primitive ();
298 }
299 
300 void
OS_file_link_soft(const char * from_name,const char * to_name)301 OS_file_link_soft (const char * from_name, const char * to_name)
302 {
303   error_unimplemented_primitive ();
304 }
305 
306 void
OS_directory_make(const char * name)307 OS_directory_make (const char * name)
308 {
309   STD_BOOL_API_CALL (CreateDirectory, (name, 0));
310 }
311 
312 void
OS_directory_delete(const char * name)313 OS_directory_delete (const char * name)
314 {
315   STD_BOOL_API_CALL (RemoveDirectory, (name));
316 }
317 
318 static void protect_fd (int fd);
319 
320 int
OS_file_touch(const char * filename)321 OS_file_touch (const char * filename)
322 {
323   int fd;
324   transaction_begin ();
325   {
326     unsigned int count = 0;
327     while (1)
328       {
329 	count += 1;
330 	/* Use O_EXCL to prevent overwriting existing file. */
331 	fd = (open (filename, (O_RDWR | O_CREAT | O_EXCL), MODE_REG));
332 	if (fd >= 0)
333 	  {
334 	    protect_fd (fd);
335 	    transaction_commit ();
336 	    return (1);
337 	  }
338 	if (errno == EEXIST)
339 	  {
340 	    fd = (open (filename, O_RDWR, MODE_REG));
341 	    if (fd >= 0)
342 	      {
343 		protect_fd (fd);
344 		break;
345 	      }
346 	    else if (errno == ENOENT)
347 	      continue;
348 	  }
349 	if (count >= FILE_TOUCH_OPEN_TRIES)
350 	  NT_error_unix_call (errno, syscall_open);
351       }
352   }
353   {
354     struct stat file_status;
355     STD_VOID_UNIX_CALL (fstat, (fd, (&file_status)));
356     if (((file_status . st_mode) & S_IFMT) != S_IFREG)
357       return (-1);
358     /* CASE 3: file length of 0 needs special treatment. */
359     if ((file_status . st_size) == 0)
360      {
361 	char buf [1];
362 	(buf[0]) = '\0';
363 	STD_VOID_UNIX_CALL (write, (fd, buf, 1));
364 	transaction_commit ();
365 	fd = (open (filename, (O_WRONLY | O_TRUNC), MODE_REG));
366 	if (fd >= 0)
367 	  STD_VOID_UNIX_CALL (close, (fd));
368 	return (0);
369       }
370   }
371   /* CASE 4: read, then write back the first byte in the file. */
372   {
373     char buf [1];
374     int scr;
375     STD_UINT_UNIX_CALL (scr, read, (fd, buf, 1));
376     if (scr > 0)
377       {
378 	STD_VOID_UNIX_CALL (lseek, (fd, 0, SEEK_SET));
379 	STD_VOID_UNIX_CALL (write, (fd, buf, 1));
380       }
381   }
382   transaction_commit ();
383   return (0);
384 }
385 
386 static void
protect_fd_close(void * ap)387 protect_fd_close (void * ap)
388 {
389   close (* ((int *) ap));
390 }
391 
392 static void
protect_fd(int fd)393 protect_fd (int fd)
394 {
395   int * p = (dstack_alloc (sizeof (int)));
396   (*p) = fd;
397   transaction_record_action (tat_always, protect_fd_close, p);
398 }
399 
400 typedef struct nt_dir_struct
401 {
402   WIN32_FIND_DATA entry;
403   HANDLE handle;         /* may be DIR_UNALLOCATED */
404   BOOL more;
405 } nt_dir;
406 
407 static nt_dir ** directory_pointers;
408 static unsigned int n_directory_pointers;
409 
410 void
NT_initialize_directory_reader(void)411 NT_initialize_directory_reader (void)
412 {
413   directory_pointers = 0;
414   n_directory_pointers = 0;
415 }
416 
417 static unsigned int
allocate_directory_pointer(nt_dir * pointer)418 allocate_directory_pointer (nt_dir * pointer)
419 {
420   if (n_directory_pointers == 0)
421     {
422       nt_dir ** pointers = (OS_malloc ((sizeof (nt_dir *)) * 4));
423       directory_pointers = pointers;
424       n_directory_pointers = 4;
425       {
426 	nt_dir ** scan = directory_pointers;
427 	nt_dir ** end = (scan + n_directory_pointers);
428 	(*scan++) = pointer;
429 	while (scan < end)
430 	  (*scan++) = 0;
431       }
432       return (0);
433     }
434   {
435     nt_dir ** scan = directory_pointers;
436     nt_dir ** end = (scan + n_directory_pointers);
437     while (scan < end)
438       if ((*scan++) == 0)
439 	{
440 	  (*--scan) = pointer;
441 	  return (scan - directory_pointers);
442 	}
443   }
444   {
445     unsigned int result = n_directory_pointers;
446     unsigned int n_pointers = (2 * n_directory_pointers);
447     nt_dir ** pointers
448       = (OS_realloc (((void *) directory_pointers),
449 		     ((sizeof (nt_dir *)) * n_pointers)));
450     {
451       nt_dir ** scan = (pointers + result);
452       nt_dir ** end = (pointers + n_pointers);
453       (*scan++) = pointer;
454       while (scan < end)
455 	(*scan++) = 0;
456     }
457     directory_pointers = pointers;
458     n_directory_pointers = n_pointers;
459     return (result);
460   }
461 }
462 
463 #define REFERENCE_DIRECTORY(index) (directory_pointers[(index)])
464 #define DEALLOCATE_DIRECTORY(index) ((directory_pointers[(index)]) = 0)
465 
466 int
OS_directory_valid_p(unsigned int index)467 OS_directory_valid_p (unsigned int index)
468 {
469   return
470     ((index < n_directory_pointers)
471      && ((REFERENCE_DIRECTORY (index)) != 0));
472 }
473 
474 unsigned int
OS_directory_open(const char * search_pattern)475 OS_directory_open (const char * search_pattern)
476 {
477   char pattern [MAX_PATH];
478   nt_dir * dir = (OS_malloc (sizeof (nt_dir)));
479   strcpy (pattern, search_pattern);
480   {
481     unsigned int len = (strlen (pattern));
482     if ((len > 0) && ((pattern [len - 1]) == '\\'))
483       strcat (pattern, "*.*");
484   }
485   (dir -> handle) = (FindFirstFile (pattern, (& (dir -> entry))));
486   if ((dir -> handle) == INVALID_HANDLE_VALUE)
487     {
488       DWORD code = (GetLastError ());
489       if (code != ERROR_FILE_NOT_FOUND)
490 	{
491 	  free (dir);
492 	  NT_error_api_call (code, apicall_FindFirstFile);
493 	}
494       (dir -> more) = FALSE;
495     }
496   else
497     (dir -> more) = TRUE;
498   return (allocate_directory_pointer (dir));
499 }
500 
501 int
win32_directory_read(unsigned int index,WIN32_FIND_DATA * info)502 win32_directory_read (unsigned int index, WIN32_FIND_DATA * info)
503 {
504   nt_dir * dir = (REFERENCE_DIRECTORY (index));
505   if ((dir == 0) || (! (dir -> more)))
506     return (0);
507   (*info) = (dir -> entry);
508   if ((dir -> handle) == INVALID_HANDLE_VALUE)
509     (dir -> more) = FALSE;
510   else
511     (dir -> more) = (FindNextFile ((dir -> handle), (& (dir -> entry))));
512   return (1);
513 }
514 
515 const char *
OS_directory_read(unsigned int index)516 OS_directory_read (unsigned int index)
517 {
518   static WIN32_FIND_DATA info;
519   return
520     ((win32_directory_read (index, (&info)))
521      ? (info . cFileName)
522      : 0);
523 }
524 
525 const char *
OS_directory_read_matching(unsigned int index,const char * prefix)526 OS_directory_read_matching (unsigned int index, const char * prefix)
527 {
528   unsigned int n = (strlen (prefix));
529   while (1)
530     {
531       const char * pathname = (OS_directory_read (index));
532       if (pathname == 0)
533 	return (0);
534       if ((strnicmp (pathname, prefix, n)) == 0)
535 	return (pathname);
536     }
537 }
538 
539 void
OS_directory_close(unsigned int index)540 OS_directory_close (unsigned int index)
541 {
542   nt_dir * dir = (REFERENCE_DIRECTORY (index));
543   if (dir)
544     {
545       if ((dir -> handle) != INVALID_HANDLE_VALUE)
546 	FindClose (dir -> handle);
547       free (dir);
548     }
549   DEALLOCATE_DIRECTORY (index);
550 }
551