]> sourceware.org Git - newlib-cygwin.git/blame - winsup/cygwin/fhandler_disk_file.cc
* fhandler_disk_file.cc (num_entries): Take . and .. into account if they do
[newlib-cygwin.git] / winsup / cygwin / fhandler_disk_file.cc
CommitLineData
7903ee69
CF
1/* fhandler_disk_file.cc
2
e136dbc2 3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
7903ee69
CF
4
5This file is part of Cygwin.
6
7This software is a copyrighted work licensed under the terms of the
8Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9details. */
10
11#include "winsup.h"
7903ee69
CF
12#include <unistd.h>
13#include <stdlib.h>
14#include <sys/cygwin.h>
15#include <signal.h>
16#include "cygerrno.h"
17#include "perprocess.h"
18#include "security.h"
19#include "cygwin/version.h"
20#include "fhandler.h"
21#include "path.h"
22#include "dtable.h"
23#include "cygheap.h"
24#include "shared_info.h"
7903ee69
CF
25#include "pinfo.h"
26#include <assert.h>
75c6a983 27#include <ctype.h>
7903ee69
CF
28
29#define _COMPILING_NEWLIB
30#include <dirent.h>
31
2402700d 32static int __stdcall
7903ee69
CF
33num_entries (const char *win32_name)
34{
35 WIN32_FIND_DATA buf;
36 HANDLE handle;
37 char buf1[MAX_PATH];
38 int count = 0;
39
40 strcpy (buf1, win32_name);
41 int len = strlen (buf1);
42 if (len == 0 || isdirsep (buf1[len - 1]))
43 strcat (buf1, "*");
44 else
45 strcat (buf1, "/*"); /* */
46
47 handle = FindFirstFileA (buf1, &buf);
48
49 if (handle == INVALID_HANDLE_VALUE)
93353aee 50 return 2; /* 2 is the minimum number of links to a dir, so... */
1aa76ad5 51 int saw_dot = 2;
7903ee69
CF
52 while (FindNextFileA (handle, &buf))
53 {
1aa76ad5
CF
54 if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
55 count++;
56 if (buf.cFileName[0] == '.'
57 && (buf.cFileName[1] == '\0'
58 || (buf.cFileName[1] == '.' && buf.cFileName[2] == '\0')))
59 saw_dot--;
7903ee69
CF
60 }
61 FindClose (handle);
1aa76ad5 62 return count + saw_dot;
7903ee69
CF
63}
64
2402700d
CF
65int __stdcall
66fhandler_disk_file::fstat_by_handle (struct __stat64 *buf, path_conv *pc)
67{
8e74c320 68 int res = 0;
2402700d
CF
69 BY_HANDLE_FILE_INFORMATION local;
70
71 /* NT 3.51 seems to have a bug when attempting to get vol serial
72 numbers. This loop gets around this. */
73 for (int i = 0; i < 2; i++)
74 {
75 if (!(res = GetFileInformationByHandle (get_handle (), &local)))
76 break;
77 if (local.dwVolumeSerialNumber && (long) local.dwVolumeSerialNumber != -1)
78 break;
79 }
80 debug_printf ("%d = GetFileInformationByHandle (%s, %d)",
81 res, get_win32_name (), get_handle ());
82 if (res == 0)
83 /* GetFileInformationByHandle will fail if it's given stdin/out/err
84 or a pipe*/
85 {
86 memset (&local, 0, sizeof (local));
87 local.nFileSizeLow = GetFileSize (get_handle (), &local.nFileSizeHigh);
88 }
89
90 return fstat_helper (buf, pc,
91 local.ftCreationTime,
92 local.ftLastAccessTime,
93 local.ftLastWriteTime,
94 local.nFileSizeHigh,
95 local.nFileSizeLow,
96 local.nFileIndexHigh,
97 local.nFileIndexLow,
98 local.nNumberOfLinks);
99}
100
101int __stdcall
102fhandler_disk_file::fstat_by_name (struct __stat64 *buf, path_conv *pc)
103{
104 int res;
105 HANDLE handle;
106 WIN32_FIND_DATA local;
107
97478fe6
CF
108 if (!pc->exists ())
109 {
b3e2d035 110 debug_printf ("already determined that pc does not exist");
97478fe6
CF
111 set_errno (ENOENT);
112 res = -1;
113 }
c17e8306 114 else if ((handle = FindFirstFile (*pc, &local)) != INVALID_HANDLE_VALUE)
2402700d 115 {
a80229fd
CV
116 FindClose (handle);
117 res = fstat_helper (buf, pc,
118 local.ftCreationTime,
119 local.ftLastAccessTime,
120 local.ftLastWriteTime,
121 local.nFileSizeHigh,
122 local.nFileSizeLow);
2402700d 123 }
c17e8306
PH
124 else if (pc->isdir ())
125 {
126 FILETIME ft = {};
127 res = fstat_helper (buf, pc, ft, ft, ft, 0, 0);
128 }
df04ae29 129 else
c17e8306
PH
130 {
131 debug_printf ("FindFirstFile failed for '%s', %E", (char *) *pc);
132 __seterrno ();
133 res = -1;
134 }
2402700d
CF
135 return res;
136}
137
138int __stdcall
acb56175 139fhandler_disk_file::fstat (struct __stat64 *buf, path_conv *pc)
7903ee69
CF
140{
141 int res = -1;
142 int oret;
7903ee69 143 int open_flags = O_RDONLY | O_BINARY | O_DIROPEN;
2402700d 144 bool query_open_already;
7903ee69 145
2402700d 146 if (get_io_handle ())
5bf785a0
CF
147 {
148 if (get_nohandle ())
149 return fstat_by_name (buf, pc);
150 else
151 return fstat_by_handle (buf, pc);
152 }
2402700d
CF
153 /* If we don't care if the file is executable or we already know if it is,
154 then just do a "query open" as it is apparently much faster. */
155 if (pc->exec_state () != dont_know_if_executable)
156 set_query_open (query_open_already = true);
157 else
158 query_open_already = false;
159
97478fe6 160 if (query_open_already && strncasematch (pc->volname (), "FAT", 3)
5bf785a0 161 && !strpbrk (get_win32_name (), "?*|<>"))
2402700d 162 oret = 0;
9f033c22
PH
163 else if (!(oret = open (pc, open_flags, 0))
164 && !query_open_already
165 && get_errno () == EACCES)
7903ee69 166 {
7903ee69
CF
167 /* If we couldn't open the file, try a "query open" with no permissions.
168 This will allow us to determine *some* things about the file, at least. */
9f033c22 169 pc->set_exec (0);
2402700d 170 set_query_open (true);
9f033c22 171 oret = open (pc, open_flags, 0);
7903ee69 172 }
2402700d 173
5bf785a0 174 if (!oret || get_nohandle ())
5d8c7a56
CF
175 res = fstat_by_name (buf, pc);
176 else
7903ee69 177 {
2402700d 178 res = fstat_by_handle (buf, pc);
7903ee69
CF
179 close ();
180 }
7903ee69
CF
181
182 return res;
183}
184
2402700d
CF
185int __stdcall
186fhandler_disk_file::fstat_helper (struct __stat64 *buf, path_conv *pc,
187 FILETIME ftCreationTime,
188 FILETIME ftLastAccessTime,
189 FILETIME ftLastWriteTime,
190 DWORD nFileSizeHigh,
191 DWORD nFileSizeLow,
192 DWORD nFileIndexHigh,
193 DWORD nFileIndexLow,
194 DWORD nNumberOfLinks)
7903ee69 195{
7903ee69 196 /* This is for FAT filesystems, which don't support atime/ctime */
2402700d
CF
197 if (ftLastAccessTime.dwLowDateTime == 0
198 && ftLastAccessTime.dwHighDateTime == 0)
199 ftLastAccessTime = ftLastWriteTime;
200 if (ftCreationTime.dwLowDateTime == 0
201 && ftCreationTime.dwHighDateTime == 0)
202 ftCreationTime = ftLastWriteTime;
203
c4e6ff48
CV
204 to_timestruc_t (&ftLastAccessTime, &buf->st_atim);
205 to_timestruc_t (&ftLastWriteTime, &buf->st_mtim);
206 to_timestruc_t (&ftCreationTime, &buf->st_ctim);
c2102631 207 buf->st_dev = pc->volser ();
1727fba0 208 buf->st_size = ((_off64_t)nFileSizeHigh << 32) + nFileSizeLow;
9f033c22
PH
209 /* The number of links to a directory includes the
210 number of subdirectories in the directory, since all
211 those subdirectories point to it.
212 This is too slow on remote drives, so we do without it.
213 Setting the count to 2 confuses `find (1)' command. So
c2102631
CF
214 let's try it with `1' as link count. */
215 if (pc->isdir () && !pc->isremote () && nNumberOfLinks == 1)
216 buf->st_nlink = num_entries (pc->get_win32 ());
217 else
218 buf->st_nlink = nNumberOfLinks;
7903ee69
CF
219
220 /* Assume that if a drive has ACL support it MAY have valid "inodes".
221 It definitely does not have valid inodes if it does not have ACL
222 support. */
2402700d
CF
223 switch (pc->has_acls () && (nFileIndexHigh || nFileIndexLow)
224 ? pc->drive_type () : DRIVE_UNKNOWN)
7903ee69
CF
225 {
226 case DRIVE_FIXED:
227 case DRIVE_REMOVABLE:
228 case DRIVE_CDROM:
229 case DRIVE_RAMDISK:
230 /* Although the documentation indicates otherwise, it seems like
231 "inodes" on these devices are persistent, at least across reboots. */
cc95baad 232 buf->st_ino = (((__ino64_t) nFileIndexHigh) << 32)
df04ae29 233 | (__ino64_t) nFileIndexLow;
7903ee69
CF
234 break;
235 default:
236 /* Either the nFileIndex* fields are unreliable or unavailable. Use the
237 next best alternative. */
238 buf->st_ino = get_namehash ();
239 break;
240 }
241
242 buf->st_blksize = S_BLKSIZE;
3a88cf1b
CV
243
244 /* GetCompressedFileSize() gets autoloaded. It returns INVALID_FILE_SIZE
245 if it doesn't exist. Since that's also a valid return value on 64bit
246 capable file systems, we must additionally check for the win32 error. */
247 nFileSizeLow = GetCompressedFileSizeA (pc->get_win32 (), &nFileSizeHigh);
248 if (nFileSizeLow != INVALID_FILE_SIZE || GetLastError () == NO_ERROR)
249 /* On systems supporting compressed (and sparsed) files,
250 GetCompressedFileSize() returns the actual amount of
251 bytes allocated on disk. */
1727fba0 252 buf->st_blocks = (((_off64_t)nFileSizeHigh << 32)
3a88cf1b
CV
253 + nFileSizeLow + S_BLKSIZE - 1) / S_BLKSIZE;
254 else
255 /* Just compute no. of blocks from file size. */
256 buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
7903ee69
CF
257
258 buf->st_mode = 0;
259 /* Using a side effect: get_file_attibutes checks for
260 directory. This is used, to set S_ISVTX, if needed. */
99138976 261 if (pc->isdir ())
7903ee69 262 buf->st_mode = S_IFDIR;
2402700d 263 else if (pc->issymlink ())
2e23862a
CV
264 {
265 /* symlinks are everything for everyone! */
266 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
267 get_file_attribute (pc->has_acls (), get_win32_name (), NULL,
268 &buf->st_uid, &buf->st_gid);
269 goto done;
270 }
2402700d 271 else if (pc->issocket ())
7903ee69 272 buf->st_mode = S_IFSOCK;
2402700d 273
2402700d 274 if (get_file_attribute (pc->has_acls (), get_win32_name (), &buf->st_mode,
2e23862a 275 &buf->st_uid, &buf->st_gid) == 0)
7903ee69
CF
276 {
277 /* If read-only attribute is set, modify ntsec return value */
99138976 278 if (pc->has_attribute (FILE_ATTRIBUTE_READONLY) && !get_symlink_p ())
7903ee69
CF
279 buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
280
281 if (!(buf->st_mode & S_IFMT))
282 buf->st_mode |= S_IFREG;
283 }
284 else
285 {
286 buf->st_mode |= STD_RBITS;
287
99138976 288 if (!pc->has_attribute (FILE_ATTRIBUTE_READONLY))
7903ee69
CF
289 buf->st_mode |= STD_WBITS;
290 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
291
b1897d27 292 if (S_ISDIR (buf->st_mode))
7903ee69
CF
293 buf->st_mode |= S_IFDIR | STD_XBITS;
294 else if (buf->st_mode & S_IFMT)
295 /* nothing */;
7903ee69 296 else
2402700d
CF
297 {
298 buf->st_mode |= S_IFREG;
281e3b24 299 if (pc->exec_state () == dont_know_if_executable)
046a4e71 300 {
281e3b24
CF
301 DWORD cur, done;
302 char magic[3];
303
304 /* First retrieve current position, set to beginning
305 of file if not already there. */
306 cur = SetFilePointer (get_handle (), 0, NULL, FILE_CURRENT);
307 if (cur != INVALID_SET_FILE_POINTER
308 && (!cur || SetFilePointer (get_handle (), 0, NULL, FILE_BEGIN)
309 != INVALID_SET_FILE_POINTER))
310 {
311 /* FIXME should we use /etc/magic ? */
312 magic[0] = magic[1] = magic[2] = '\0';
313 if (ReadFile (get_handle (), magic, 3, &done, NULL)
314 && has_exec_chars (magic, done))
315 {
316 set_execable_p ();
317 pc->set_exec ();
318 buf->st_mode |= STD_XBITS;
319 }
320 (void) SetFilePointer (get_handle (), cur, NULL, FILE_BEGIN);
321 }
046a4e71 322 }
046a4e71 323 }
281e3b24
CF
324
325 if (pc->exec_state () == is_executable)
326 buf->st_mode |= STD_XBITS;
2311f6b0
CV
327
328 /* This fakes the permissions of all files to match the current umask. */
329 buf->st_mode &= ~(cygheap->umask);
046a4e71 330 }
7dcbb978 331
2e23862a 332 done:
2402700d
CF
333 syscall_printf ("0 = fstat (, %p) st_atime=%x st_size=%D, st_mode=%p, st_ino=%d, sizeof=%d",
334 buf, buf->st_atime, buf->st_size, buf->st_mode,
335 (int) buf->st_ino, sizeof (*buf));
7903ee69
CF
336 return 0;
337}
338
97a2e075
CF
339fhandler_disk_file::fhandler_disk_file (DWORD devtype) :
340 fhandler_base (devtype)
341{
342}
343
7903ee69 344fhandler_disk_file::fhandler_disk_file () :
97a2e075 345 fhandler_base (FH_DISK)
7903ee69
CF
346{
347}
348
349int
350fhandler_disk_file::open (path_conv *real_path, int flags, mode_t mode)
351{
352 if (real_path->case_clash && flags & O_CREAT)
353 {
97a2e075 354 debug_printf ("case clash detected");
7903ee69
CF
355 set_errno (ECASECLASH);
356 return 0;
357 }
358
7903ee69
CF
359 set_has_acls (real_path->has_acls ());
360 set_isremote (real_path->isremote ());
361
335556d5 362 int res = fhandler_base::open (real_path, flags | O_DIROPEN, mode);
7903ee69
CF
363 if (!res)
364 goto out;
365
366 /* This is for file systems known for having a buggy CreateFile call
367 which might return a valid HANDLE without having actually opened
368 the file.
369 The only known file system to date is the SUN NFS Solstice Client 3.1
370 which returns a valid handle when trying to open a file in a nonexistent
371 directory. */
2402700d 372 if (real_path->has_buggy_open () && !real_path->exists ())
7903ee69
CF
373 {
374 debug_printf ("Buggy open detected.");
375 close ();
376 set_errno (ENOENT);
377 return 0;
378 }
379
5cd82412
CF
380 /* Attributes may be set only if a file is _really_ created.
381 This code is now only used for ntea here since the files
382 security attributes are set in CreateFile () now. */
383 if (flags & O_CREAT
384 && GetLastError () != ERROR_ALREADY_EXISTS
385 && !allow_ntsec && allow_ntea)
386 set_file_attribute (has_acls (), get_win32_name (), mode);
387
fac297d5 388 set_fs_flags (real_path->fs_flags ());
7903ee69
CF
389 set_symlink_p (real_path->issymlink ());
390 set_execable_p (real_path->exec_state ());
391 set_socket_p (real_path->issocket ());
392
393out:
394 syscall_printf ("%d = fhandler_disk_file::open (%s, %p)", res,
395 get_win32_name (), flags);
396 return res;
397}
398
399int
400fhandler_disk_file::close ()
401{
335556d5 402 int res = fhandler_base::close ();
7903ee69
CF
403 if (!res)
404 cygwin_shared->delqueue.process_queue ();
405 return res;
406}
407
408/*
409 * FIXME !!!
410 * The correct way to do this to get POSIX locking
411 * semantics is to keep a linked list of posix lock
412 * requests and map them into Win32 locks. The problem
413 * is that Win32 does not deal correctly with overlapping
414 * lock requests. Also another pain is that Win95 doesn't do
415 * non-blocking or non exclusive locks at all. For '95 just
416 * convert all lock requests into blocking,exclusive locks.
417 * This shouldn't break many apps but denying all locking
418 * would.
419 * For now just convert to Win32 locks and hope for the best.
420 */
421
422int
423fhandler_disk_file::lock (int cmd, struct flock *fl)
424{
1727fba0 425 _off64_t win32_start;
7903ee69
CF
426 int win32_len;
427 DWORD win32_upper;
1727fba0 428 _off64_t startpos;
7903ee69
CF
429
430 /*
431 * We don't do getlck calls yet.
432 */
433
434 if (cmd == F_GETLK)
435 {
436 set_errno (ENOSYS);
437 return -1;
438 }
439
440 /*
441 * Calculate where in the file to start from,
442 * then adjust this by fl->l_start.
443 */
444
445 switch (fl->l_whence)
446 {
447 case SEEK_SET:
448 startpos = 0;
449 break;
450 case SEEK_CUR:
acb56175 451 if ((startpos = lseek (0, SEEK_CUR)) == ILLEGAL_SEEK)
7903ee69
CF
452 return -1;
453 break;
454 case SEEK_END:
455 {
456 BY_HANDLE_FILE_INFORMATION finfo;
c90e1cf1 457 if (GetFileInformationByHandle (get_handle (), &finfo) == 0)
7903ee69
CF
458 {
459 __seterrno ();
460 return -1;
461 }
1727fba0 462 startpos = ((_off64_t)finfo.nFileSizeHigh << 32)
acb56175 463 + finfo.nFileSizeLow;
7903ee69
CF
464 break;
465 }
466 default:
467 set_errno (EINVAL);
468 return -1;
469 }
470
471 /*
472 * Now the fun starts. Adjust the start and length
473 * fields until they make sense.
474 */
475
476 win32_start = startpos + fl->l_start;
477 if (fl->l_len < 0)
478 {
479 win32_start -= fl->l_len;
480 win32_len = -fl->l_len;
481 }
482 else
483 win32_len = fl->l_len;
484
485 if (win32_start < 0)
486 {
487 /* watch the signs! */
488 win32_len -= -win32_start;
489 if (win32_len <= 0)
490 {
491 /* Failure ! */
492 set_errno (EINVAL);
493 return -1;
494 }
495 win32_start = 0;
496 }
497
498 /*
499 * Special case if len == 0 for POSIX means lock
500 * to the end of the entire file (and all future extensions).
501 */
502 if (win32_len == 0)
503 {
504 win32_len = 0xffffffff;
505 win32_upper = wincap.lock_file_highword ();
506 }
507 else
508 win32_upper = 0;
509
510 BOOL res;
511
512 if (wincap.has_lock_file_ex ())
513 {
514 DWORD lock_flags = (cmd == F_SETLK) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
515 lock_flags |= (fl->l_type == F_WRLCK) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
516
517 OVERLAPPED ov;
518
519 ov.Internal = 0;
520 ov.InternalHigh = 0;
521 ov.Offset = (DWORD)win32_start;
522 ov.OffsetHigh = 0;
523 ov.hEvent = (HANDLE) 0;
524
525 if (fl->l_type == F_UNLCK)
526 {
527 res = UnlockFileEx (get_handle (), 0, (DWORD)win32_len, win32_upper, &ov);
528 }
529 else
530 {
531 res = LockFileEx (get_handle (), lock_flags, 0, (DWORD)win32_len,
532 win32_upper, &ov);
533 /* Deal with the fail immediately case. */
534 /*
535 * FIXME !! I think this is the right error to check for
536 * but I must admit I haven't checked....
537 */
538 if ((res == 0) && (lock_flags & LOCKFILE_FAIL_IMMEDIATELY) &&
539 (GetLastError () == ERROR_LOCK_FAILED))
540 {
541 set_errno (EAGAIN);
542 return -1;
543 }
544 }
545 }
546 else
547 {
548 /* Windows 95 -- use primitive lock call */
549 if (fl->l_type == F_UNLCK)
550 res = UnlockFile (get_handle (), (DWORD)win32_start, 0, (DWORD)win32_len,
551 win32_upper);
552 else
553 res = LockFile (get_handle (), (DWORD)win32_start, 0, (DWORD)win32_len, win32_upper);
554 }
555
556 if (res == 0)
557 {
558 __seterrno ();
559 return -1;
560 }
561
562 return 0;
563}
564
565DIR *
97a2e075 566fhandler_disk_file::opendir (path_conv& real_name)
7903ee69 567{
7903ee69
CF
568 DIR *dir;
569 DIR *res = NULL;
570 size_t len;
571
97a2e075 572 if (!real_name.isdir ())
7903ee69
CF
573 set_errno (ENOTDIR);
574 else if ((len = strlen (real_name))> MAX_PATH - 3)
575 set_errno (ENAMETOOLONG);
576 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
577 set_errno (ENOMEM);
578 else if ((dir->__d_dirname = (char *) malloc (len + 3)) == NULL)
579 {
580 free (dir);
581 set_errno (ENOMEM);
582 }
583 else if ((dir->__d_dirent =
584 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
585 {
586 free (dir->__d_dirname);
587 free (dir);
588 set_errno (ENOMEM);
589 }
590 else
591 {
592 strcpy (dir->__d_dirname, real_name.get_win32 ());
593 dir->__d_dirent->d_version = __DIRENT_VERSION;
594 cygheap_fdnew fd;
518f5d49 595 if (fd >= 0)
a113a3c5 596 {
518f5d49
CV
597 fd = this;
598 fd->set_nohandle (true);
599 dir->__d_dirent->d_fd = fd;
0c7b5572 600 dir->__fh = this;
518f5d49
CV
601 /* FindFirstFile doesn't seem to like duplicate /'s. */
602 len = strlen (dir->__d_dirname);
603 if (len == 0 || isdirsep (dir->__d_dirname[len - 1]))
604 strcat (dir->__d_dirname, "*");
605 else
606 strcat (dir->__d_dirname, "\\*"); /**/
607 dir->__d_cookie = __DIRENT_COOKIE;
0c7b5572 608 dir->__handle = INVALID_HANDLE_VALUE;
518f5d49
CV
609 dir->__d_position = 0;
610 dir->__d_dirhash = get_namehash ();
611
612 res = dir;
613 }
3f214783
CF
614 if (real_name.isencoded ())
615 set_encoded ();
7903ee69
CF
616 }
617
97a2e075 618 syscall_printf ("%p = opendir (%s)", res, get_name ());
7903ee69
CF
619 return res;
620}
621
622struct dirent *
623fhandler_disk_file::readdir (DIR *dir)
624{
625 WIN32_FIND_DATA buf;
626 HANDLE handle;
627 struct dirent *res = NULL;
628
0c7b5572 629 if (dir->__handle == INVALID_HANDLE_VALUE
7903ee69
CF
630 && dir->__d_position == 0)
631 {
632 handle = FindFirstFileA (dir->__d_dirname, &buf);
633 DWORD lasterr = GetLastError ();
0c7b5572 634 dir->__handle = handle;
7903ee69
CF
635 if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES))
636 {
637 seterrno_from_win_error (__FILE__, __LINE__, lasterr);
638 return res;
639 }
640 }
0c7b5572 641 else if (dir->__handle == INVALID_HANDLE_VALUE)
3f214783 642 return res;
0c7b5572 643 else if (!FindNextFileA (dir->__handle, &buf))
7903ee69
CF
644 {
645 DWORD lasterr = GetLastError ();
0c7b5572
CF
646 (void) FindClose (dir->__handle);
647 dir->__handle = INVALID_HANDLE_VALUE;
7903ee69
CF
648 /* POSIX says you shouldn't set errno when readdir can't
649 find any more files; so, if another error we leave it set. */
650 if (lasterr != ERROR_NO_MORE_FILES)
651 seterrno_from_win_error (__FILE__, __LINE__, lasterr);
652 syscall_printf ("%p = readdir (%p)", res, dir);
653 return res;
654 }
655
656 /* We get here if `buf' contains valid data. */
3f214783
CF
657 if (get_encoded ())
658 (void) fnunmunge (dir->__d_dirent->d_name, buf.cFileName);
659 else
660 strcpy (dir->__d_dirent->d_name, buf.cFileName);
7903ee69
CF
661
662 /* Check for Windows shortcut. If it's a Cygwin or U/WIN
663 symlink, drop the .lnk suffix. */
664 if (buf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
665 {
666 char *c = dir->__d_dirent->d_name;
667 int len = strlen (c);
668 if (strcasematch (c + len - 4, ".lnk"))
669 {
670 char fbuf[MAX_PATH + 1];
671 strcpy (fbuf, dir->__d_dirname);
672 strcpy (fbuf + strlen (fbuf) - 1, dir->__d_dirent->d_name);
673 path_conv fpath (fbuf, PC_SYM_NOFOLLOW);
674 if (fpath.issymlink ())
675 c[len - 4] = '\0';
676 }
677 }
678
97a2e075 679 dir->__d_position++;
7903ee69
CF
680 res = dir->__d_dirent;
681 syscall_printf ("%p = readdir (%p) (%s)",
682 &dir->__d_dirent, dir, buf.cFileName);
683 return res;
684}
685
1727fba0 686_off64_t
7903ee69
CF
687fhandler_disk_file::telldir (DIR *dir)
688{
689 return dir->__d_position;
690}
691
692void
1727fba0 693fhandler_disk_file::seekdir (DIR *dir, _off64_t loc)
7903ee69
CF
694{
695 rewinddir (dir);
696 while (loc > dir->__d_position)
97a2e075 697 if (!readdir (dir))
7903ee69
CF
698 break;
699}
700
701void
702fhandler_disk_file::rewinddir (DIR *dir)
703{
0c7b5572 704 if (dir->__handle != INVALID_HANDLE_VALUE)
7903ee69 705 {
0c7b5572
CF
706 (void) FindClose (dir->__handle);
707 dir->__handle = INVALID_HANDLE_VALUE;
7903ee69
CF
708 }
709 dir->__d_position = 0;
710}
711
712int
713fhandler_disk_file::closedir (DIR *dir)
714{
715 int res = 0;
0c7b5572
CF
716 if (dir->__handle != INVALID_HANDLE_VALUE &&
717 FindClose (dir->__handle) == 0)
7903ee69
CF
718 {
719 __seterrno ();
720 res = -1;
721 }
722 syscall_printf ("%d = closedir (%p)", res, dir);
723 return 0;
724}
97a2e075
CF
725
726fhandler_cygdrive::fhandler_cygdrive (int unit) :
727 fhandler_disk_file (FH_CYGDRIVE), unit (unit), ndrives (0), pdrive (NULL)
728{
729}
730
731#define DRVSZ sizeof ("x:\\")
732void
733fhandler_cygdrive::set_drives ()
734{
0c7b5572
CF
735 const int len = 2 + 26 * DRVSZ;
736 char *p = (char *) crealloc ((void *) win32_path_name, len);
97a2e075 737
ff938546
CF
738 win32_path_name = pdrive = p;
739 ndrives = GetLogicalDriveStrings (len, p) / DRVSZ;
97a2e075
CF
740}
741
742int
acb56175 743fhandler_cygdrive::fstat (struct __stat64 *buf, path_conv *pc)
97a2e075
CF
744{
745 if (!iscygdrive_root ())
746 return fhandler_disk_file::fstat (buf, pc);
747 buf->st_mode = S_IFDIR | 0555;
748 if (!ndrives)
749 set_drives ();
1aa76ad5 750 buf->st_nlink = ndrives + 2;
97a2e075
CF
751 return 0;
752}
753
754DIR *
755fhandler_cygdrive::opendir (path_conv& real_name)
756{
757 DIR *dir;
758
759 dir = fhandler_disk_file::opendir (real_name);
760 if (dir && iscygdrive_root () && !ndrives)
761 set_drives ();
762
763 return dir;
764}
765
766struct dirent *
767fhandler_cygdrive::readdir (DIR *dir)
768{
769 if (!iscygdrive_root ())
770 return fhandler_disk_file::readdir (dir);
771 if (!pdrive || !*pdrive)
3fea2e40 772 return NULL;
1aa76ad5 773 if (GetFileAttributes (pdrive) == INVALID_FILE_ATTRIBUTES)
28af03fa 774 {
5ba08a92 775 pdrive = strchr (pdrive, '\0') + 1;
ded25056
CF
776 return readdir (dir);
777 }
1aa76ad5
CF
778
779 *dir->__d_dirent->d_name = cyg_tolower (*pdrive);
780 dir->__d_dirent->d_name[1] = '\0';
97a2e075 781 dir->__d_position++;
5ba08a92 782 pdrive = strchr (pdrive, '\0') + 1;
97a2e075 783 syscall_printf ("%p = readdir (%p) (%s)", &dir->__d_dirent, dir,
5bf785a0 784 dir->__d_dirent->d_name);
97a2e075
CF
785 return dir->__d_dirent;
786}
787
1727fba0 788_off64_t
97a2e075
CF
789fhandler_cygdrive::telldir (DIR *dir)
790{
791 return fhandler_disk_file::telldir (dir);
792}
793
794void
1727fba0 795fhandler_cygdrive::seekdir (DIR *dir, _off64_t loc)
97a2e075
CF
796{
797 if (!iscygdrive_root ())
798 return fhandler_disk_file::seekdir (dir, loc);
799
5ba08a92
CF
800 for (pdrive = win32_path_name, dir->__d_position = -1; *pdrive;
801 pdrive = strchr (pdrive, '\0') + 1)
97a2e075
CF
802 if (++dir->__d_position >= loc)
803 break;
804
805 return;
806}
807
808void
809fhandler_cygdrive::rewinddir (DIR *dir)
810{
811 if (!iscygdrive_root ())
812 return fhandler_disk_file::rewinddir (dir);
813 pdrive = win32_path_name;
814 dir->__d_position = 0;
815 return;
816}
817
818int
819fhandler_cygdrive::closedir (DIR *dir)
820{
821 if (!iscygdrive_root ())
822 return fhandler_disk_file::closedir (dir);
823 pdrive = win32_path_name;
824 return -1;
825}
This page took 0.222444 seconds and 5 git commands to generate.