OSDN Git Service

Remove some more unneeded 'return;'s throughout.
[pf3gnuchains/sourceware.git] / winsup / cygwin / fhandler_disk_file.cc
1 /* fhandler_disk_file.cc
2
3    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4    2005 Red Hat, Inc.
5
6 This file is part of Cygwin.
7
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10 details. */
11
12 #include "winsup.h"
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <sys/cygwin.h>
16 #include <sys/acl.h>
17 #include <signal.h>
18 #include "cygerrno.h"
19 #include "perprocess.h"
20 #include "security.h"
21 #include "cygwin/version.h"
22 #include "path.h"
23 #include "fhandler.h"
24 #include "dtable.h"
25 #include "cygheap.h"
26 #include "shared_info.h"
27 #include "pinfo.h"
28 #include <ntdef.h>
29 #include "ntdll.h"
30 #include <assert.h>
31 #include <ctype.h>
32 #include <winioctl.h>
33
34 #define _COMPILING_NEWLIB
35 #include <dirent.h>
36
37 unsigned __stdcall
38 path_conv::ndisk_links (DWORD nNumberOfLinks)
39 {
40   if (!isdir () || isremote ())
41     return nNumberOfLinks;
42
43   int len = strlen (*this);
44   char fn[len + 3];
45   strcpy (fn, *this);
46
47   const char *s;
48   unsigned count;
49   if (nNumberOfLinks <= 1)
50     {
51       s = "/*";
52       count = 0;
53     }
54   else
55     {
56       s = "/..";
57       count = nNumberOfLinks;
58     }
59
60   if (len == 0 || isdirsep (fn[len - 1]))
61     strcpy (fn + len, s + 1);
62   else
63     strcpy (fn + len, s);
64
65   WIN32_FIND_DATA buf;
66   HANDLE h = FindFirstFile (fn, &buf);
67
68   int saw_dot = 2;
69   if (h != INVALID_HANDLE_VALUE)
70     {
71       if (nNumberOfLinks > 1)
72         saw_dot--;
73       else
74         do
75           {
76             if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
77               count++;
78             if (buf.cFileName[0] == '.'
79                 && (buf.cFileName[1] == '\0'
80                     || (buf.cFileName[1] == '.' && buf.cFileName[2] == '\0')))
81               saw_dot--;
82           }
83         while (FindNextFileA (h, &buf));
84       FindClose (h);
85     }
86
87   if (nNumberOfLinks > 1)
88     {
89       fn[len + 2] = '\0';
90       h = FindFirstFile (fn, &buf);
91       if (h)
92         saw_dot--;
93       FindClose (h);
94     }
95
96   return count + saw_dot;
97 }
98
99 int __stdcall
100 fhandler_base::fstat_by_handle (struct __stat64 *buf)
101 {
102   BY_HANDLE_FILE_INFORMATION local;
103
104   if (wincap.is_winnt ())
105     {
106       NTSTATUS status;
107       IO_STATUS_BLOCK io;
108       /* The entries potentially contain a name of MAX_PATH wide characters. */
109       DWORD fvi_size = 2 * CYG_MAX_PATH + sizeof (FILE_FS_VOLUME_INFORMATION);
110       DWORD fai_size = 2 * CYG_MAX_PATH + sizeof (FILE_ALL_INFORMATION);
111
112       PFILE_FS_VOLUME_INFORMATION pfvi = (PFILE_FS_VOLUME_INFORMATION)
113                                          alloca (fvi_size);
114       PFILE_ALL_INFORMATION pfai = (PFILE_ALL_INFORMATION) alloca (fai_size);
115
116       status = NtQueryVolumeInformationFile (get_handle (), &io, pfvi, fvi_size,
117                                              FileFsVolumeInformation);
118       if (!NT_SUCCESS (status))
119         {
120           debug_printf ("%u = NtQueryVolumeInformationFile)",
121                         RtlNtStatusToDosError (status));
122           pfvi->VolumeSerialNumber = 0; /* Set to pc.volser () in helper. */
123         }
124       status = NtQueryInformationFile (get_handle (), &io, pfai, fai_size,
125                                        FileAllInformation);
126       if (NT_SUCCESS (status))
127         /* If the change time is 0, it's a file system which doesn't
128            support a change timestamp.  In that case use the LastWriteTime
129            entry, as in other calls to fstat_helper. */
130         return fstat_helper (buf,
131                          pfai->BasicInformation.ChangeTime.QuadPart ?
132                          *(FILETIME *) &pfai->BasicInformation.ChangeTime :
133                          *(FILETIME *) &pfai->BasicInformation.LastWriteTime,
134                          *(FILETIME *) &pfai->BasicInformation.LastAccessTime,
135                          *(FILETIME *) &pfai->BasicInformation.LastWriteTime,
136                          pfvi->VolumeSerialNumber,
137                          pfai->StandardInformation.EndOfFile.HighPart,
138                          pfai->StandardInformation.EndOfFile.LowPart,
139                          pfai->StandardInformation.AllocationSize.QuadPart,
140                          pfai->InternalInformation.IndexNumber.HighPart,
141                          pfai->InternalInformation.IndexNumber.LowPart,
142                          pfai->StandardInformation.NumberOfLinks);
143
144       debug_printf ("%u = NtQueryInformationFile)",
145                     RtlNtStatusToDosError (status));
146     }
147
148   BOOL res = GetFileInformationByHandle (get_handle (), &local);
149   debug_printf ("%d = GetFileInformationByHandle (%s, %d)",
150                 res, get_win32_name (), get_handle ());
151   /* GetFileInformationByHandle will fail if it's given stdio handle or pipe.
152      It also fails on 9x when trying to access directories on shares. */
153   if (!res)
154     {
155       memset (&local, 0, sizeof (local));
156       local.nFileSizeLow = GetFileSize (get_handle (), &local.nFileSizeHigh);
157       /* Even GetFileSize fails on 9x when trying to access directories
158          on shares. In this case reset filesize to 0. */
159       if (local.nFileSizeLow == 0xffffffff && GetLastError ())
160         local.nFileSizeLow = 0;
161     }
162
163   return fstat_helper (buf,
164                        local.ftLastWriteTime, /* see fstat_helper comment */
165                        local.ftLastAccessTime,
166                        local.ftLastWriteTime,
167                        local.dwVolumeSerialNumber,
168                        local.nFileSizeHigh,
169                        local.nFileSizeLow,
170                        -1LL,
171                        local.nFileIndexHigh,
172                        local.nFileIndexLow,
173                        local.nNumberOfLinks);
174 }
175
176 int __stdcall
177 fhandler_base::fstat_by_name (struct __stat64 *buf)
178 {
179   int res;
180   HANDLE handle;
181   WIN32_FIND_DATA local;
182
183   if (!pc.exists ())
184     {
185       debug_printf ("already determined that pc does not exist");
186       set_errno (ENOENT);
187       res = -1;
188     }
189   else if ((handle = FindFirstFile (pc, &local)) != INVALID_HANDLE_VALUE)
190     {
191       FindClose (handle);
192       res = fstat_helper (buf,
193                           local.ftLastWriteTime, /* see fstat_helper comment */
194                           local.ftLastAccessTime,
195                           local.ftLastWriteTime,
196                           pc.volser (),
197                           local.nFileSizeHigh,
198                           local.nFileSizeLow,
199                           -1LL,
200                           0,
201                           0,
202                           1);
203     }
204   else if (pc.isdir ())
205     {
206       FILETIME ft = {};
207       res = fstat_helper (buf, ft, ft, ft, pc.volser (), 0, 0, -1LL, 0, 0, 1);
208     }
209   else
210     {
211       debug_printf ("FindFirstFile failed for '%s', %E", (char *) pc);
212       __seterrno ();
213       res = -1;
214     }
215   return res;
216 }
217
218 int __stdcall
219 fhandler_base::fstat_fs (struct __stat64 *buf)
220 {
221   int res = -1;
222   int oret;
223   int open_flags = O_RDONLY | O_BINARY;
224
225   if (get_io_handle ())
226     {
227       if (nohandle () || is_fs_special ())
228         return fstat_by_name (buf);
229       else
230         return fstat_by_handle (buf);
231     }
232   /* If we don't care if the file is executable or we already know if it is,
233      then just do a "query open" as it is apparently much faster. */
234   if (pc.exec_state () != dont_know_if_executable)
235     {
236       if (pc.fs_is_fat () && !strpbrk (get_win32_name (), "?*|<>"))
237         return fstat_by_name (buf);
238       query_open (query_stat_control);
239     }
240   if (!(oret = open_fs (open_flags, 0)) && get_errno () == EACCES)
241     {
242       /* If we couldn't open the file, try a query open with no permissions.
243          This allows us to determine *some* things about the file, at least. */
244       pc.set_exec (0);
245       query_open (query_read_control);
246       oret = open_fs (open_flags, 0);
247     }
248
249   if (oret)
250     {
251       /* We now have a valid handle, regardless of the "nohandle" state.
252          Since fhandler_base::open only calls CloseHandle if !nohandle,
253          we have to set it to false before calling close_fs and restore
254          the state afterwards. */
255       res = fstat_by_handle (buf);
256       bool no_handle = nohandle ();
257       nohandle (false);
258       close_fs ();
259       nohandle (no_handle);
260       set_io_handle (NULL);
261     }
262   else
263     res = fstat_by_name (buf);
264
265   return res;
266 }
267
268 /* The ftChangeTime is taken from the NTFS ChangeTime entry, if reading
269    the file information using NtQueryInformationFile succeeded.  If not,
270    it's faked using the LastWriteTime entry from GetFileInformationByHandle
271    or FindFirstFile.  We're deliberatly not using the creation time anymore
272    to simplify interaction with native Windows applications which choke on
273    creation times >= access or write times. */
274 int __stdcall
275 fhandler_base::fstat_helper (struct __stat64 *buf,
276                              FILETIME ftChangeTime,
277                              FILETIME ftLastAccessTime,
278                              FILETIME ftLastWriteTime,
279                              DWORD dwVolumeSerialNumber,
280                              DWORD nFileSizeHigh,
281                              DWORD nFileSizeLow,
282                              LONGLONG nAllocSize,
283                              DWORD nFileIndexHigh,
284                              DWORD nFileIndexLow,
285                              DWORD nNumberOfLinks)
286 {
287   IO_STATUS_BLOCK st;
288   FILE_COMPRESSION_INFORMATION fci;
289
290   to_timestruc_t (&ftLastAccessTime, &buf->st_atim);
291   to_timestruc_t (&ftLastWriteTime, &buf->st_mtim);
292   to_timestruc_t (&ftChangeTime, &buf->st_ctim);
293   buf->st_dev = dwVolumeSerialNumber ?: pc.volser ();
294   buf->st_size = ((_off64_t) nFileSizeHigh << 32) + nFileSizeLow;
295   /* The number of links to a directory includes the
296      number of subdirectories in the directory, since all
297      those subdirectories point to it.
298      This is too slow on remote drives, so we do without it.
299      Setting the count to 2 confuses `find (1)' command. So
300      let's try it with `1' as link count. */
301   buf->st_nlink = pc.ndisk_links (nNumberOfLinks);
302
303   /* Assume that if a drive has ACL support it MAY have valid "inodes".
304      It definitely does not have valid inodes if it does not have ACL
305      support. */
306   switch (pc.has_acls () && (nFileIndexHigh || nFileIndexLow)
307           ? pc.drive_type () : DRIVE_UNKNOWN)
308     {
309     case DRIVE_FIXED:
310     case DRIVE_REMOVABLE:
311     case DRIVE_CDROM:
312     case DRIVE_RAMDISK:
313       /* Although the documentation indicates otherwise, it seems like
314          "inodes" on these devices are persistent, at least across reboots. */
315       buf->st_ino = (((__ino64_t) nFileIndexHigh) << 32)
316                     | (__ino64_t) nFileIndexLow;
317       break;
318     default:
319       /* Either the nFileIndex* fields are unreliable or unavailable.  Use the
320          next best alternative. */
321       buf->st_ino = get_namehash ();
322       break;
323     }
324
325   buf->st_blksize = S_BLKSIZE;
326
327   if (nAllocSize >= 0LL)
328     /* A successful NtQueryInformationFile returns the allocation size
329        correctly for compressed and sparse files as well. */
330     buf->st_blocks = (nAllocSize + S_BLKSIZE - 1) / S_BLKSIZE;
331   else if (pc.has_attribute (FILE_ATTRIBUTE_COMPRESSED
332                              | FILE_ATTRIBUTE_SPARSE_FILE)
333       && get_io_handle () && !is_fs_special ()
334       && !NtQueryInformationFile (get_io_handle (), &st, (PVOID) &fci,
335                                   sizeof fci, FileCompressionInformation))
336     /* Otherwise we request the actual amount of bytes allocated for
337        compressed and sparsed files. */
338     buf->st_blocks = (fci.CompressedSize.QuadPart + S_BLKSIZE - 1) / S_BLKSIZE;
339   else
340     /* Otherwise compute no. of blocks from file size. */
341     buf->st_blocks  = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
342
343   buf->st_mode = 0;
344   /* Using a side effect: get_file_attibutes checks for
345      directory. This is used, to set S_ISVTX, if needed.  */
346   if (pc.isdir ())
347     buf->st_mode = S_IFDIR;
348   else if (pc.issymlink ())
349     {
350       buf->st_size = pc.get_symlink_length ();
351       /* symlinks are everything for everyone! */
352       buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
353       get_file_attribute (pc.has_acls (), get_io_handle (), get_win32_name (),
354                           NULL, &buf->st_uid, &buf->st_gid);
355       goto done;
356     }
357   else if (pc.issocket ())
358     buf->st_mode = S_IFSOCK;
359
360   if (!get_file_attribute (pc.has_acls (), is_fs_special () ? NULL: get_io_handle (),
361                            get_win32_name (), &buf->st_mode, &buf->st_uid, &buf->st_gid))
362     {
363       /* If read-only attribute is set, modify ntsec return value */
364       if (pc.has_attribute (FILE_ATTRIBUTE_READONLY) && !pc.issymlink ())
365         buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
366
367       if (buf->st_mode & S_IFMT)
368         /* nothing */;
369       else if (!is_fs_special ())
370         buf->st_mode |= S_IFREG;
371       else
372         {
373           buf->st_dev = dev ();
374           buf->st_mode = dev ().mode;
375         }
376     }
377   else
378     {
379       buf->st_mode |= STD_RBITS;
380
381       if (!pc.has_attribute (FILE_ATTRIBUTE_READONLY) && !pc.issymlink ())
382         buf->st_mode |= STD_WBITS;
383       /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
384
385       if (S_ISDIR (buf->st_mode))
386         buf->st_mode |= S_IFDIR | STD_XBITS;
387       else if (buf->st_mode & S_IFMT)
388         /* nothing */;
389       else if (is_fs_special ())
390         {
391           buf->st_dev = dev ();
392           buf->st_mode = dev ().mode;
393         }
394       else
395         {
396           buf->st_mode |= S_IFREG;
397           if (pc.exec_state () == dont_know_if_executable)
398             {
399               DWORD cur, done;
400               char magic[3];
401
402               /* First retrieve current position, set to beginning
403                  of file if not already there. */
404               cur = SetFilePointer (get_handle (), 0, NULL, FILE_CURRENT);
405               if (cur != INVALID_SET_FILE_POINTER
406                   && (!cur || SetFilePointer (get_handle (), 0, NULL, FILE_BEGIN)
407                       != INVALID_SET_FILE_POINTER))
408                 {
409                   /* FIXME should we use /etc/magic ? */
410                   magic[0] = magic[1] = magic[2] = '\0';
411                   if (ReadFile (get_handle (), magic, 3, &done, NULL)
412                       && has_exec_chars (magic, done))
413                     {
414                       pc.set_exec ();
415                       buf->st_mode |= STD_XBITS;
416                     }
417                   SetFilePointer (get_handle (), cur, NULL, FILE_BEGIN);
418                 }
419             }
420         }
421
422       if (pc.exec_state () == is_executable)
423         buf->st_mode |= STD_XBITS;
424
425       /* This fakes the permissions of all files to match the current umask. */
426       buf->st_mode &= ~(cygheap->umask);
427     }
428
429  done:
430   syscall_printf ("0 = fstat (, %p) st_atime=%x st_size=%D, st_mode=%p, st_ino=%d, sizeof=%d",
431                   buf, buf->st_atime, buf->st_size, buf->st_mode,
432                   (int) buf->st_ino, sizeof (*buf));
433   return 0;
434 }
435
436 int __stdcall
437 fhandler_disk_file::fstat (struct __stat64 *buf)
438 {
439   /* Changing inode data requires setting ctime (only 9x). */
440   if (has_changed ())
441     touch_ctime ();
442   return fstat_fs (buf);
443 }
444
445 void
446 fhandler_disk_file::touch_ctime ()
447 {
448   FILETIME ft;
449
450   GetSystemTimeAsFileTime (&ft);
451   /* Modification time is touched if the file data has changed as well.
452      This happens for instance on write() or ftruncate(). */
453   if (!SetFileTime (get_io_handle (), NULL, NULL, &ft))
454     debug_printf ("SetFileTime (%s) failed, %E", get_win32_name ());
455   else
456     has_changed (false);
457 }
458
459 int __stdcall
460 fhandler_disk_file::fchmod (mode_t mode)
461 {
462   extern int chmod_device (path_conv& pc, mode_t mode);
463   int res = -1;
464   int oret = 0;
465
466   if (pc.is_fs_special ())
467     return chmod_device (pc, mode);
468
469   /* Also open on 9x, otherwise we can't touch ctime. */
470   if (!get_io_handle ())
471     {
472       query_open (query_write_control);
473       if (!(oret = open (O_BINARY, 0)))
474         {
475           /* If the file couldn't be opened, that's really only a problem if
476              ACLs or EAs should get written. */
477           if ((allow_ntsec && pc.has_acls ()) || allow_ntea)
478             return -1;
479         }
480     }
481
482   if ((allow_ntsec && pc.has_acls ()) || allow_ntea)
483     {
484       if (!allow_ntsec && allow_ntea) /* Not necessary when manipulating SD. */
485         SetFileAttributes (pc, (DWORD) pc & ~FILE_ATTRIBUTE_READONLY);
486       if (pc.isdir ())
487         mode |= S_IFDIR;
488       if (!set_file_attribute (pc.has_acls (), get_io_handle (), pc,
489                                ILLEGAL_UID, ILLEGAL_GID, mode)
490           && allow_ntsec)
491         res = 0;
492     }
493
494   /* if the mode we want has any write bits set, we can't be read only. */
495   if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
496     pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
497   else
498     pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
499
500   if (!SetFileAttributes (pc, pc))
501     __seterrno ();
502   else if (!allow_ntsec || !pc.has_acls ())
503     /* Correct NTFS security attributes have higher priority */
504     res = 0;
505
506   /* Set ctime on success. */
507   if (!res && !wincap.is_winnt ())
508     has_changed (true);
509
510   if (oret)
511     close ();
512
513   return res;
514 }
515
516 int __stdcall
517 fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
518 {
519   int oret = 0;
520
521   if (!pc.has_acls () || !allow_ntsec)
522     {
523       /* fake - if not supported, pretend we're like win95
524          where it just works */
525       return 0;
526     }
527
528   if (!get_io_handle ())
529     {
530       query_open (query_write_control);
531       if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
532         return -1;
533     }
534
535   mode_t attrib = 0;
536   if (pc.isdir ())
537     attrib |= S_IFDIR;
538   int res = get_file_attribute (pc.has_acls (), get_io_handle (), pc, &attrib);
539   if (!res)
540     res = set_file_attribute (pc.has_acls (), get_io_handle (), pc,
541                               uid, gid, attrib);
542
543   if (oret)
544     close ();
545
546   return res;
547 }
548
549 int _stdcall
550 fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp)
551 {
552   int res = -1;
553   int oret = 0;
554
555   if (!pc.has_acls () || !allow_ntsec)
556     {
557       switch (cmd)
558         {
559           struct __stat64 st;
560
561           case SETACL:
562             /* Open for writing required to be able to set ctime
563                (even though setting the ACL is just pretended). */
564             if (!get_io_handle ())
565               oret = open (O_WRONLY | O_BINARY, 0);
566             res = 0;
567             break;
568           case GETACL:
569             if (!aclbufp)
570               set_errno(EFAULT);
571             else if (nentries < MIN_ACL_ENTRIES)
572               set_errno (ENOSPC);
573             else
574               {
575                 if (!get_io_handle ())
576                   {
577                     query_open (query_read_control);
578                     if (!(oret = open (O_BINARY, 0)))
579                       return -1;
580                   }
581                 if (!fstat_by_handle (&st))
582                   {
583                     aclbufp[0].a_type = USER_OBJ;
584                     aclbufp[0].a_id = st.st_uid;
585                     aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
586                     aclbufp[1].a_type = GROUP_OBJ;
587                     aclbufp[1].a_id = st.st_gid;
588                     aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
589                     aclbufp[2].a_type = OTHER_OBJ;
590                     aclbufp[2].a_id = ILLEGAL_GID;
591                     aclbufp[2].a_perm = st.st_mode & S_IRWXO;
592                     aclbufp[3].a_type = CLASS_OBJ;
593                     aclbufp[3].a_id = ILLEGAL_GID;
594                     aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
595                     res = MIN_ACL_ENTRIES;
596                   }
597               }
598             break;
599           case GETACLCNT:
600             res = MIN_ACL_ENTRIES;
601             break;
602           default:
603             set_errno (EINVAL);
604             break;
605         }
606     }
607   else
608     {
609       if (!get_io_handle ())
610         {
611           query_open (cmd == SETACL ? query_write_control : query_read_control);
612           if (!(oret = open (O_BINARY, 0)))
613             return -1;
614         }
615       switch (cmd)
616         {
617           case SETACL:
618             if (!aclsort32 (nentries, 0, aclbufp))
619               res = setacl (get_io_handle (), pc, nentries, aclbufp);
620             break;
621           case GETACL:
622             if (!aclbufp)
623               set_errno(EFAULT);
624             else
625               res = getacl (get_io_handle (), pc, pc, nentries, aclbufp);
626             break;
627           case GETACLCNT:
628             res = getacl (get_io_handle (), pc, pc, 0, NULL);
629             break;
630           default:
631             set_errno (EINVAL);
632             break;
633         }
634     }
635
636   if (oret)
637     close ();
638
639   return res;
640 }
641
642 int
643 fhandler_disk_file::ftruncate (_off64_t length)
644 {
645   int res = -1, res_bug = 0;
646
647   if (length < 0 || !get_output_handle ())
648     set_errno (EINVAL);
649   else if (pc.isdir ())
650     set_errno (EISDIR);
651   else if (!(get_access () & GENERIC_WRITE))
652     set_errno (EBADF);
653   else
654     {
655       _off64_t prev_loc = lseek (0, SEEK_CUR);
656       if (lseek (length, SEEK_SET) >= 0)
657         {
658           if (get_fs_flags (FILE_SUPPORTS_SPARSE_FILES))
659             {
660               _off64_t actual_length;
661               DWORD size_high = 0;
662               actual_length = GetFileSize (get_output_handle (), &size_high);
663               actual_length += ((_off64_t) size_high) << 32;
664               if (length >= actual_length + (128 * 1024))
665                 {
666                   DWORD dw;
667                   BOOL r = DeviceIoControl (get_output_handle (),
668                                             FSCTL_SET_SPARSE, NULL, 0, NULL,
669                                             0, &dw, NULL);
670                   syscall_printf ("%d = DeviceIoControl(%p, FSCTL_SET_SPARSE)",
671                                   r, get_output_handle ());
672                 }
673             }
674           else if (wincap.has_lseek_bug ())
675             res_bug = write (&res, 0);
676           if (!SetEndOfFile (get_output_handle ()))
677             __seterrno ();
678           else
679             res = res_bug;
680           /* restore original file pointer location */
681           lseek (prev_loc, SEEK_SET);
682         }
683     }
684   return res;
685 }
686
687 int
688 fhandler_disk_file::link (const char *newpath)
689 {
690   path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX);
691   extern bool allow_winsymlinks;
692
693   if (newpc.error)
694     {
695       set_errno (newpc.case_clash ? ECASECLASH : newpc.error);
696       return -1;
697     }
698
699   if (newpc.exists ())
700     {
701       syscall_printf ("file '%s' exists?", (char *) newpc);
702       set_errno (EEXIST);
703       return -1;
704     }
705
706   if (newpc[strlen (newpc) - 1] == '.')
707     {
708       syscall_printf ("trailing dot, bailing out");
709       set_errno (EINVAL);
710       return -1;
711     }
712
713   /* Shortcut hack. */
714   char new_lnk_buf[CYG_MAX_PATH + 5];
715   if (allow_winsymlinks && pc.is_lnk_special () && !newpc.case_clash)
716     {
717       strcpy (new_lnk_buf, newpath);
718       strcat (new_lnk_buf, ".lnk");
719       newpath = new_lnk_buf;
720       newpc.check (newpath, PC_SYM_NOFOLLOW);
721     }
722
723   query_open (query_write_attributes);
724   if (!open (O_BINARY, 0))
725     {
726       syscall_printf ("Opening file failed");
727       __seterrno ();
728       return -1;
729     }
730
731   /* Try to make hard link first on Windows NT */
732   if (wincap.has_hard_links ())
733     {
734       if (CreateHardLinkA (newpc, pc, NULL))
735         goto success;
736
737       /* There are two cases to consider:
738          - The FS doesn't support hard links ==> ERROR_INVALID_FUNCTION
739            We copy the file.
740          - CreateHardLinkA is not supported  ==> ERROR_PROC_NOT_FOUND
741            In that case (<= NT4) we try the old-style method.
742          Any other error should be taken seriously. */
743       if (GetLastError () == ERROR_INVALID_FUNCTION)
744         {
745           syscall_printf ("FS doesn't support hard links: Copy file");
746           goto docopy;
747         }
748       if (GetLastError () != ERROR_PROC_NOT_FOUND)
749         {
750           syscall_printf ("CreateHardLinkA failed");
751           __seterrno ();
752           close ();
753           return -1;
754         }
755
756       WIN32_STREAM_ID stream_id;
757       LPVOID context;
758       WCHAR wbuf[CYG_MAX_PATH];
759       BOOL ret;
760       DWORD written, write_err, path_len, size;
761
762       path_len = sys_mbstowcs (wbuf, newpc, CYG_MAX_PATH) * sizeof (WCHAR);
763
764       stream_id.dwStreamId = BACKUP_LINK;
765       stream_id.dwStreamAttributes = 0;
766       stream_id.dwStreamNameSize = 0;
767       stream_id.Size.HighPart = 0;
768       stream_id.Size.LowPart = path_len;
769       size = sizeof (WIN32_STREAM_ID) - sizeof (WCHAR**)
770              + stream_id.dwStreamNameSize;
771       context = NULL;
772       write_err = 0;
773       /* Write WIN32_STREAM_ID */
774       ret = BackupWrite (get_handle (), (LPBYTE) &stream_id, size,
775                          &written, FALSE, FALSE, &context);
776       if (ret)
777         {
778           /* write the buffer containing the path */
779           /* FIXME: BackupWrite sometimes traps if linkname is invalid.
780              Need to handle. */
781           ret = BackupWrite (get_handle (), (LPBYTE) wbuf, path_len,
782                              &written, FALSE, FALSE, &context);
783           if (!ret)
784             {
785               write_err = GetLastError ();
786               syscall_printf ("cannot write linkname, %E");
787             }
788           /* Free context */
789           BackupWrite (get_handle (), NULL, 0, &written,
790                        TRUE, FALSE, &context);
791         }
792       else
793         {
794           write_err = GetLastError ();
795           syscall_printf ("cannot write stream_id, %E");
796         }
797
798       if (!ret)
799         {
800           /* Only copy file if FS doesn't support hard links */
801           if (write_err == ERROR_INVALID_FUNCTION)
802             {
803               syscall_printf ("FS doesn't support hard links: Copy file");
804               goto docopy;
805             }
806
807           close ();
808           __seterrno_from_win_error (write_err);
809           return -1;
810         }
811
812     success:
813       close ();
814       if (!allow_winsymlinks && pc.is_lnk_special ())
815         SetFileAttributes (newpc, (DWORD) pc
816                                    | FILE_ATTRIBUTE_SYSTEM
817                                    | FILE_ATTRIBUTE_READONLY);
818       return 0;
819     }
820 docopy:
821   /* do this with a copy */
822   if (!CopyFileA (pc, newpc, 1))
823     {
824       __seterrno ();
825       return -1;
826     }
827   /* Set ctime on success (copy gets it automatically). */
828   if (!wincap.is_winnt ())
829     has_changed (true);
830   close ();
831   fhandler_disk_file fh (newpc);
832   fh.query_open (query_write_attributes);
833   if (fh.open (O_BINARY, 0))
834     fh.close ();
835   return 0;
836 }
837
838 int
839 fhandler_disk_file::utimes (const struct timeval *tvp)
840 {
841   return utimes_fs (tvp);
842 }
843
844 int
845 fhandler_base::utimes_fs (const struct timeval *tvp)
846 {
847   FILETIME lastaccess, lastwrite;
848   struct timeval tmp[2];
849
850   query_open (query_write_attributes);
851   if (!open_fs (O_BINARY, 0))
852     {
853       /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
854          to change the timestamps.  Unfortunately it's not sufficient for a
855          remote HPFS which requires GENERIC_WRITE, so we just retry to open
856          for writing, though this fails for R/O files of course. */
857       query_open (no_query);
858       if (!open_fs (O_WRONLY | O_BINARY, 0))
859         {
860           syscall_printf ("Opening file failed");
861           return -1;
862         }
863     }
864
865   if (nohandle ())      /* Directory query_open on 9x. */
866     return 0;
867
868   gettimeofday (&tmp[0], 0);
869   if (!tvp)
870     {
871       tmp[1] = tmp[0];
872       tvp = tmp;
873     }
874   timeval_to_filetime (&tvp[0], &lastaccess);
875   timeval_to_filetime (&tvp[1], &lastwrite);
876   debug_printf ("incoming lastaccess %08x %08x", tvp[0].tv_sec, tvp[0].tv_usec);
877
878   if (is_fs_special ())
879     SetFileAttributes (pc, (DWORD) pc & ~FILE_ATTRIBUTE_READONLY);
880   BOOL res = SetFileTime (get_handle (), NULL, &lastaccess, &lastwrite);
881   DWORD errcode = GetLastError ();
882   if (is_fs_special ())
883     SetFileAttributes (pc, pc);
884   /* Opening a directory on a 9x share from a NT machine works(!), but
885      then the SetFileTimes fails with ERROR_NOT_SUPPORTED.  Oh well... */
886   if (!res && errcode != ERROR_NOT_SUPPORTED)
887     {
888       close ();
889       __seterrno_from_win_error (errcode);
890       return -1;
891     }
892
893   close ();
894   return 0;
895 }
896
897 fhandler_disk_file::fhandler_disk_file () :
898   fhandler_base ()
899 {
900 }
901
902 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
903   fhandler_base ()
904 {
905   set_name (pc);
906 }
907
908 int
909 fhandler_disk_file::open (int flags, mode_t mode)
910 {
911   return open_fs (flags, mode);
912 }
913
914 int
915 fhandler_base::open_fs (int flags, mode_t mode)
916 {
917   if (pc.case_clash && flags & O_CREAT)
918     {
919       debug_printf ("case clash detected");
920       set_errno (ECASECLASH);
921       return 0;
922     }
923
924   /* Unfortunately NT allows to open directories for writing, but that's
925      disallowed according to SUSv3. */
926   if (pc.isdir () && (flags & (O_WRONLY | O_RDWR)))
927     {
928       set_errno (EISDIR);
929       return 0;
930     }
931
932   int res = fhandler_base::open (flags | O_DIROPEN, mode);
933   if (!res)
934     goto out;
935
936   /* This is for file systems known for having a buggy CreateFile call
937      which might return a valid HANDLE without having actually opened
938      the file.
939      The only known file system to date is the SUN NFS Solstice Client 3.1
940      which returns a valid handle when trying to open a file in a nonexistent
941      directory. */
942   if (pc.has_buggy_open () && !pc.exists ())
943     {
944       debug_printf ("Buggy open detected.");
945       close_fs ();
946       set_errno (ENOENT);
947       return 0;
948     }
949
950   /* Attributes may be set only if a file is _really_ created.
951      This code is now only used for ntea here since the files
952      security attributes are set in CreateFile () now. */
953   if (flags & O_CREAT
954       && GetLastError () != ERROR_ALREADY_EXISTS
955       && !allow_ntsec && allow_ntea)
956     set_file_attribute (false, NULL, get_win32_name (), mode);
957
958   set_fs_flags (pc.fs_flags ());
959
960 out:
961   syscall_printf ("%d = fhandler_disk_file::open (%s, %p)", res,
962                   get_win32_name (), flags);
963   return res;
964 }
965
966 int
967 fhandler_disk_file::close ()
968 {
969   if (!hExeced)
970     {
971       /* Changing inode data requires setting ctime (only 9x). */
972       if (has_changed ())
973         touch_ctime ();
974     }
975   return close_fs ();
976 }
977
978 int
979 fhandler_base::close_fs ()
980 {
981   int res = fhandler_base::close ();
982   if (!res)
983     user_shared->delqueue.process_queue ();
984   return res;
985 }
986
987 ssize_t __stdcall
988 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
989 {
990   ssize_t res;
991   _off64_t curpos = lseek (0, SEEK_CUR);
992   if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
993     res = -1;
994   else
995     {
996       size_t tmp_count = count;
997       read (buf, tmp_count);
998       if (lseek (curpos, SEEK_SET) == 0)
999         res = (ssize_t) tmp_count;
1000       else
1001         res = -1;
1002     }
1003   debug_printf ("%d = pread (%p, %d, %d)\n", res, buf, count, offset);
1004   return res;
1005 }
1006
1007 ssize_t __stdcall
1008 fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
1009 {
1010   int res;
1011   _off64_t curpos = lseek (0, SEEK_CUR);
1012   if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1013     res = curpos;
1014   else
1015     {
1016       res = (ssize_t) write (buf, count);
1017       if (lseek (curpos, SEEK_SET) < 0)
1018         res = -1;
1019     }
1020   debug_printf ("%d = pwrite (%p, %d, %d)\n", res, buf, count, offset);
1021   return res;
1022 }
1023
1024 /* FIXME: The correct way to do this to get POSIX locking semantics is to
1025    keep a linked list of posix lock requests and map them into Win32 locks.
1026    he problem is that Win32 does not deal correctly with overlapping lock
1027    requests. Also another pain is that Win95 doesn't do non-blocking or
1028    non-exclusive locks at all. For '95 just convert all lock requests into
1029    blocking,exclusive locks.  This shouldn't break many apps but denying all
1030    locking would.  For now just convert to Win32 locks and hope for
1031    the best.  */
1032
1033 int
1034 fhandler_disk_file::lock (int cmd, struct __flock64 *fl)
1035 {
1036   _off64_t win32_start;
1037   _off64_t win32_len;
1038   _off64_t startpos;
1039
1040   /*
1041    * We don't do getlck calls yet.
1042    */
1043
1044   if (cmd == F_GETLK)
1045     {
1046       set_errno (ENOSYS);
1047       return -1;
1048     }
1049
1050   /*
1051    * Calculate where in the file to start from,
1052    * then adjust this by fl->l_start.
1053    */
1054
1055   switch (fl->l_whence)
1056     {
1057       case SEEK_SET:
1058         startpos = 0;
1059         break;
1060       case SEEK_CUR:
1061         if ((startpos = lseek (0, SEEK_CUR)) == ILLEGAL_SEEK)
1062           return -1;
1063         break;
1064       case SEEK_END:
1065         {
1066           BY_HANDLE_FILE_INFORMATION finfo;
1067           if (GetFileInformationByHandle (get_handle (), &finfo) == 0)
1068             {
1069               __seterrno ();
1070               return -1;
1071             }
1072           startpos = ((_off64_t)finfo.nFileSizeHigh << 32)
1073                      + finfo.nFileSizeLow;
1074           break;
1075         }
1076       default:
1077         set_errno (EINVAL);
1078         return -1;
1079     }
1080
1081   /*
1082    * Now the fun starts. Adjust the start and length
1083    *  fields until they make sense.
1084    */
1085
1086   win32_start = startpos + fl->l_start;
1087   if (fl->l_len < 0)
1088     {
1089       win32_start -= fl->l_len;
1090       win32_len = -fl->l_len;
1091     }
1092   else
1093     win32_len = fl->l_len;
1094
1095   if (win32_start < 0)
1096     {
1097       /* watch the signs! */
1098       win32_len -= -win32_start;
1099       if (win32_len <= 0)
1100         {
1101           /* Failure ! */
1102           set_errno (EINVAL);
1103           return -1;
1104         }
1105       win32_start = 0;
1106     }
1107
1108   DWORD off_high, off_low, len_high, len_low;
1109
1110   off_low = (DWORD)(win32_start & UINT32_MAX);
1111   off_high = (DWORD)(win32_start >> 32);
1112   if (win32_len == 0)
1113     {
1114       /* Special case if len == 0 for POSIX means lock to the end of
1115          the entire file (and all future extensions).  */
1116       /* CV, 2003-12-03: And yet another Win 9x bugginess.  For some reason
1117          offset + length must be <= 0x100000000.  I'm using 0xffffffff as
1118          upper border here, this should be sufficient. */
1119       len_low = UINT32_MAX - (wincap.lock_file_highword () ? 0 : off_low);
1120       len_high = wincap.lock_file_highword ();
1121     }
1122   else
1123     {
1124       len_low = (DWORD)(win32_len & UINT32_MAX);
1125       len_high = (DWORD)(win32_len >> 32);
1126     }
1127
1128   BOOL res;
1129
1130   if (wincap.has_lock_file_ex ())
1131     {
1132       DWORD lock_flags = (cmd == F_SETLK) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
1133       lock_flags |= (fl->l_type == F_WRLCK) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
1134
1135       OVERLAPPED ov;
1136
1137       ov.Internal = 0;
1138       ov.InternalHigh = 0;
1139       ov.Offset = off_low;
1140       ov.OffsetHigh = off_high;
1141       ov.hEvent = (HANDLE) 0;
1142
1143       if (fl->l_type == F_UNLCK)
1144         {
1145           res = UnlockFileEx (get_handle (), 0, len_low, len_high, &ov);
1146         }
1147       else
1148         {
1149           res = LockFileEx (get_handle (), lock_flags, 0,
1150                             len_low, len_high, &ov);
1151           /* Deal with the fail immediately case. */
1152           /*
1153            * FIXME !! I think this is the right error to check for
1154            * but I must admit I haven't checked....
1155            */
1156           if ((res == 0) && (lock_flags & LOCKFILE_FAIL_IMMEDIATELY) &&
1157                             (GetLastError () == ERROR_LOCK_FAILED))
1158             {
1159               set_errno (EAGAIN);
1160               return -1;
1161             }
1162         }
1163     }
1164   else
1165     {
1166       /* Windows 95 -- use primitive lock call */
1167       if (fl->l_type == F_UNLCK)
1168         res = UnlockFile (get_handle (), off_low, off_high, len_low, len_high);
1169       else
1170         res = LockFile (get_handle (), off_low, off_high, len_low, len_high);
1171     }
1172
1173   if (res == 0)
1174     {
1175       __seterrno ();
1176       return -1;
1177     }
1178
1179   return 0;
1180 }
1181
1182 int
1183 fhandler_disk_file::mkdir (mode_t mode)
1184 {
1185   int res = -1;
1186   SECURITY_ATTRIBUTES sa = sec_none_nih;
1187   security_descriptor sd;
1188
1189   if (allow_ntsec && has_acls ())
1190     set_security_attribute (S_IFDIR | ((mode & 07777) & ~cygheap->umask),
1191                             &sa, sd);
1192
1193   if (CreateDirectoryA (get_win32_name (), &sa))
1194     {
1195       if (!allow_ntsec && allow_ntea)
1196         set_file_attribute (false, NULL, get_win32_name (),
1197                             S_IFDIR | ((mode & 07777) & ~cygheap->umask));
1198 #ifdef HIDDEN_DOT_FILES
1199       char *c = strrchr (real_dir.get_win32 (), '\\');
1200       if ((c && c[1] == '.') || *get_win32_name () == '.')
1201         SetFileAttributes (get_win32_name (), FILE_ATTRIBUTE_HIDDEN);
1202 #endif
1203       res = 0;
1204     }
1205   else
1206     __seterrno ();
1207
1208   return res;
1209 }
1210
1211 int
1212 fhandler_disk_file::rmdir ()
1213 {
1214   int res = -1;
1215
1216   /* Even own directories can't be removed if R/O attribute is set. */
1217   if (pc.has_attribute (FILE_ATTRIBUTE_READONLY))
1218     SetFileAttributes (get_win32_name (),
1219                        (DWORD) pc & ~FILE_ATTRIBUTE_READONLY);
1220
1221   for (bool is_cwd = false; ; is_cwd = true)
1222     {
1223       DWORD err, att = 0;
1224       int rc = RemoveDirectory (get_win32_name ());
1225
1226       if (isremote () && exists ())
1227         att = GetFileAttributes (get_win32_name ());
1228
1229       /* Sometimes smb indicates failure when it really succeeds, so check for
1230          this case specifically. */
1231       if (rc || att == INVALID_FILE_ATTRIBUTES)
1232         {
1233           /* RemoveDirectory on a samba drive doesn't return an error if the
1234              directory can't be removed because it's not empty. Checking for
1235              existence afterwards keeps us informed about success. */
1236           if (!isremote () || att == INVALID_FILE_ATTRIBUTES)
1237             {
1238               res = 0;
1239               break;
1240             }
1241           err = ERROR_DIR_NOT_EMPTY;
1242         }
1243       else
1244         err = GetLastError ();
1245
1246       /* This kludge detects if we are attempting to remove the current working
1247          directory.  If so, we will move elsewhere to potentially allow the
1248          rmdir to succeed.  This means that cygwin's concept of the current working
1249          directory != Windows concept but, hey, whaddaregonnado?
1250          Note that this will not cause something like the following to work:
1251          $ cd foo
1252          $ rmdir .
1253          since the shell will have foo "open" in the above case and so Windows will
1254          not allow the deletion. (Actually it does on 9X.)
1255          FIXME: A potential workaround for this is for cygwin apps to *never* call
1256          SetCurrentDirectory. */
1257
1258       extern char windows_system_directory[];
1259       if (strcasematch (get_win32_name (), cygheap->cwd.win32)
1260           && !strcasematch (windows_system_directory, cygheap->cwd.win32)
1261           && !is_cwd
1262           && SetCurrentDirectory (windows_system_directory))
1263         continue;
1264
1265       /* On 9X ERROR_ACCESS_DENIED is returned
1266          if you try to remove a non-empty directory. */
1267       if (err == ERROR_ACCESS_DENIED
1268           && wincap.access_denied_on_delete ())
1269         err = ERROR_DIR_NOT_EMPTY;
1270
1271       __seterrno_from_win_error (err);
1272
1273       /* Directory still exists, restore its characteristics. */
1274       if (pc.has_attribute (FILE_ATTRIBUTE_READONLY))
1275         SetFileAttributes (get_win32_name (), (DWORD) pc);
1276       if (is_cwd)
1277         SetCurrentDirectory (get_win32_name ());
1278       break;
1279     }
1280
1281   return res;
1282 }
1283
1284 DIR *
1285 fhandler_disk_file::opendir ()
1286 {
1287   DIR *dir;
1288   DIR *res = NULL;
1289   size_t len;
1290   path_conv rootdir ("/");
1291
1292   if (!pc.isdir ())
1293     set_errno (ENOTDIR);
1294   else if ((len = strlen (pc)) > CYG_MAX_PATH - 3)
1295     set_errno (ENAMETOOLONG);
1296   else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1297     set_errno (ENOMEM);
1298   else if ((dir->__d_dirname = (char *) malloc (len + 3)) == NULL)
1299     {
1300       set_errno (ENOMEM);
1301       goto free_dir;
1302     }
1303   else if ((dir->__d_dirent =
1304             (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1305     {
1306       set_errno (ENOMEM);
1307       goto free_dirname;
1308     }
1309   else if (fhaccess (R_OK) != 0)
1310     goto free_dirent;
1311   else
1312     {
1313       strcpy (dir->__d_dirname, get_win32_name ());
1314       dir->__d_dirent->d_version = __DIRENT_VERSION;
1315       cygheap_fdnew fd;
1316
1317       if (fd < 0)
1318         goto free_dirent;
1319
1320       fd = this;
1321       fd->nohandle (true);
1322       dir->__d_dirent->d_fd = fd;
1323       dir->__fh = this;
1324       /* FindFirstFile doesn't seem to like duplicate /'s. */
1325       len = strlen (dir->__d_dirname);
1326       if (len == 0 || isdirsep (dir->__d_dirname[len - 1]))
1327         strcat (dir->__d_dirname, "*");
1328       else
1329         strcat (dir->__d_dirname, "\\*");  /**/
1330       dir->__d_cookie = __DIRENT_COOKIE;
1331       dir->__handle = INVALID_HANDLE_VALUE;
1332       dir->__d_position = 0;
1333       dir->__d_dirhash = get_namehash ();
1334
1335       res = dir;
1336       dir->__flags = strcasematch (pc, rootdir) ? dirent_isroot : 0;
1337     }
1338
1339   syscall_printf ("%p = opendir (%s)", res, get_name ());
1340   return res;
1341
1342 free_dirent:
1343   free (dir->__d_dirent);
1344 free_dirname:
1345   free (dir->__d_dirname);
1346 free_dir:
1347   free (dir);
1348   return res;
1349 }
1350
1351 int
1352 fhandler_disk_file::readdir (DIR *dir, dirent *de)
1353 {
1354   WIN32_FIND_DATA buf;
1355   HANDLE handle;
1356   int res;
1357
1358   if (!dir->__handle)
1359     {
1360       res = ENMFILE;
1361       goto out;
1362     }
1363   if (dir->__handle == INVALID_HANDLE_VALUE && dir->__d_position == 0)
1364     {
1365       handle = FindFirstFileA (dir->__d_dirname, &buf);
1366       DWORD lasterr = GetLastError ();
1367       dir->__handle = handle;
1368       if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES))
1369         {
1370           res = geterrno_from_win_error ();
1371           goto out;
1372         }
1373     }
1374   else if (dir->__handle == INVALID_HANDLE_VALUE)
1375     {
1376       res = EBADF;
1377       goto out;
1378     }
1379   else if (!FindNextFileA (dir->__handle, &buf))
1380     {
1381       bool added = false;
1382       if (!(dir->__flags & dirent_isroot))
1383         /* nothing */;
1384       else if (0 && !(dir->__flags & dirent_saw_dev))
1385         {
1386           strcpy (buf.cFileName, "dev");
1387           added = true;
1388         }
1389       else if (!(dir->__flags & dirent_saw_proc))
1390         {
1391           strcpy (buf.cFileName, "proc");
1392           added = true;
1393         }
1394       else if (!(dir->__flags & dirent_saw_cygdrive)
1395                && mount_table->cygdrive_len > 1)
1396         {
1397           strcpy (buf.cFileName, mount_table->cygdrive + 1);
1398           buf.cFileName[mount_table->cygdrive_len - 2] = '\0';
1399           added = true;
1400         }
1401
1402       if (added)
1403         buf.dwFileAttributes = 0;
1404       else
1405         {
1406           res = geterrno_from_win_error ();
1407           FindClose (dir->__handle);
1408           dir->__handle = NULL;
1409           goto out;
1410         }
1411     }
1412
1413   /* Check for Windows shortcut. If it's a Cygwin or U/WIN
1414      symlink, drop the .lnk suffix. */
1415   if (buf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
1416     {
1417       char *c = buf.cFileName;
1418       int len = strlen (c);
1419       if (strcasematch (c + len - 4, ".lnk"))
1420         {
1421           char fbuf[CYG_MAX_PATH];
1422           strcpy (fbuf, dir->__d_dirname);
1423           strcpy (fbuf + strlen (fbuf) - 1, c);
1424           path_conv fpath (fbuf, PC_SYM_NOFOLLOW);
1425           if (fpath.issymlink () || fpath.isspecial ())
1426             c[len - 4] = '\0';
1427         }
1428     }
1429
1430   /* We get here if `buf' contains valid data.  */
1431   if (pc.isencoded ())
1432     fnunmunge (de->d_name, buf.cFileName);
1433   else
1434     strcpy (de->d_name, buf.cFileName);
1435   if (dir->__flags && dirent_isroot)
1436     {
1437       if (strcasematch (de->d_name, "dev"))
1438         dir->__flags |= dirent_saw_dev;
1439       else if (strcasematch (de->d_name, "proc"))
1440         dir->__flags |= dirent_saw_proc;
1441       if (strlen (de->d_name) == mount_table->cygdrive_len - 2
1442           && strncasematch (de->d_name, mount_table->cygdrive + 1,
1443                             mount_table->cygdrive_len - 2))
1444         dir->__flags |= dirent_saw_cygdrive;
1445     }
1446
1447   dir->__d_position++;
1448   res = 0;
1449 out:
1450   syscall_printf ("%d = readdir (%p) (%s)", dir, &de, de->d_name);
1451   return res;
1452 }
1453
1454 _off64_t
1455 fhandler_disk_file::telldir (DIR *dir)
1456 {
1457   return dir->__d_position;
1458 }
1459
1460 void
1461 fhandler_disk_file::seekdir (DIR *dir, _off64_t loc)
1462 {
1463   rewinddir (dir);
1464   while (loc > dir->__d_position)
1465     if (!::readdir (dir))
1466       break;
1467 }
1468
1469 void
1470 fhandler_disk_file::rewinddir (DIR *dir)
1471 {
1472   if (dir->__handle != INVALID_HANDLE_VALUE)
1473     {
1474       if (dir->__handle)
1475         FindClose (dir->__handle);
1476       dir->__handle = INVALID_HANDLE_VALUE;
1477     }
1478   dir->__d_position = 0;
1479 }
1480
1481 int
1482 fhandler_disk_file::closedir (DIR *dir)
1483 {
1484   int res = 0;
1485   if (dir->__handle && dir->__handle != INVALID_HANDLE_VALUE
1486       && FindClose (dir->__handle) == 0)
1487     {
1488       __seterrno ();
1489       res = -1;
1490     }
1491   syscall_printf ("%d = closedir (%p)", res, dir);
1492   return 0;
1493 }
1494
1495 fhandler_cygdrive::fhandler_cygdrive () :
1496   fhandler_disk_file (), ndrives (0), pdrive (NULL)
1497 {
1498 }
1499
1500 #define DRVSZ sizeof ("x:\\")
1501 void
1502 fhandler_cygdrive::set_drives ()
1503 {
1504   const int len = 2 + 26 * DRVSZ;
1505   char *p = const_cast<char *> (get_win32_name ());
1506   pdrive = p;
1507   ndrives = GetLogicalDriveStrings (len, p) / DRVSZ;
1508 }
1509
1510 int
1511 fhandler_cygdrive::fstat (struct __stat64 *buf)
1512 {
1513   buf->st_mode = S_IFDIR | 0555;
1514   if (!ndrives)
1515     set_drives ();
1516   buf->st_nlink = ndrives + 2;
1517   return 0;
1518 }
1519
1520 DIR *
1521 fhandler_cygdrive::opendir ()
1522 {
1523   DIR *dir;
1524
1525   dir = fhandler_disk_file::opendir ();
1526   if (dir && !ndrives)
1527     set_drives ();
1528
1529   return dir;
1530 }
1531
1532 int
1533 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
1534 {
1535   if (!pdrive || !*pdrive)
1536     return ENMFILE;
1537   if (GetFileAttributes (pdrive) == INVALID_FILE_ATTRIBUTES)
1538     {
1539       pdrive = strchr (pdrive, '\0') + 1;
1540       return readdir (dir, de);
1541     }
1542
1543   *de->d_name = cyg_tolower (*pdrive);
1544   de->d_name[1] = '\0';
1545   dir->__d_position++;
1546   pdrive = strchr (pdrive, '\0') + 1;
1547   syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
1548   return 0;
1549 }
1550
1551 void
1552 fhandler_cygdrive::rewinddir (DIR *dir)
1553 {
1554   pdrive = get_win32_name ();
1555   dir->__d_position = 0;
1556 }
1557
1558 int
1559 fhandler_cygdrive::closedir (DIR *dir)
1560 {
1561   pdrive = get_win32_name ();
1562   return 0;
1563 }