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 (§ors_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