OSDN Git Service

* fhandler_process.cc: Drop unneeded includes.
[pf3gnuchains/pf3gnuchains4x.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, 2006, 2007, 2008, 2009, 2010, 2011 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 <stdlib.h>
14 #include <sys/acl.h>
15 #include <sys/statvfs.h>
16 #include "cygerrno.h"
17 #include "security.h"
18 #include "path.h"
19 #include "fhandler.h"
20 #include "dtable.h"
21 #include "cygheap.h"
22 #include "shared_info.h"
23 #include "pinfo.h"
24 #include "ntdll.h"
25 #include "tls_pbuf.h"
26 #include "pwdgrp.h"
27 #include <winioctl.h>
28
29 #define _COMPILING_NEWLIB
30 #include <dirent.h>
31
32 class __DIR_mounts
33 {
34   int            count;
35   const char    *parent_dir;
36   int            parent_dir_len;
37   UNICODE_STRING mounts[MAX_MOUNTS];
38   bool           found[MAX_MOUNTS + 2];
39   UNICODE_STRING cygdrive;
40
41 #define __DIR_PROC      (MAX_MOUNTS)
42 #define __DIR_CYGDRIVE  (MAX_MOUNTS+1)
43
44   __ino64_t eval_ino (int idx)
45     {
46       __ino64_t ino = 0;
47       char fname[parent_dir_len + mounts[idx].Length + 2];
48       struct __stat64 st;
49
50       char *c = stpcpy (fname, parent_dir);
51       if (c[- 1] != '/')
52         *c++ = '/';
53       sys_wcstombs (c, mounts[idx].Length + 1,
54                     mounts[idx].Buffer, mounts[idx].Length / sizeof (WCHAR));
55       path_conv pc (fname, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE);
56       if (!stat_worker (pc, &st))
57         ino = st.st_ino;
58       return ino;
59     }
60
61 public:
62   __DIR_mounts (const char *posix_path)
63   : parent_dir (posix_path)
64     {
65       parent_dir_len = strlen (parent_dir);
66       count = mount_table->get_mounts_here (parent_dir, parent_dir_len, mounts,
67                                             &cygdrive);
68       rewind ();
69     }
70   ~__DIR_mounts ()
71     {
72       for (int i = 0; i < count; ++i)
73         RtlFreeUnicodeString (&mounts[i]);
74       RtlFreeUnicodeString (&cygdrive);
75     }
76   __ino64_t check_mount (PUNICODE_STRING fname, __ino64_t ino,
77                          bool eval = true)
78     {
79       if (parent_dir_len == 1)  /* root dir */
80         {
81           if (RtlEqualUnicodeString (fname, &ro_u_proc, FALSE))
82             {
83               found[__DIR_PROC] = true;
84               return 2;
85             }
86           if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2
87               && RtlEqualUnicodeString (fname, &cygdrive, FALSE))
88             {
89               found[__DIR_CYGDRIVE] = true;
90               return 2;
91             }
92         }
93       for (int i = 0; i < count; ++i)
94         if (RtlEqualUnicodeString (fname, &mounts[i], FALSE))
95           {
96             found[i] = true;
97             return eval ? eval_ino (i) : 1;
98           }
99       return ino;
100     }
101   __ino64_t check_missing_mount (PUNICODE_STRING retname = NULL)
102     {
103       for (int i = 0; i < count; ++i)
104         if (!found[i])
105           {
106             found[i] = true;
107             if (retname)
108               {
109                 *retname = mounts[i];
110                 return eval_ino (i);
111               }
112             return 1;
113           }
114       if (parent_dir_len == 1)  /* root dir */
115         {
116           if (!found[__DIR_PROC])
117             {
118               found[__DIR_PROC] = true;
119               if (retname)
120                 *retname = ro_u_proc;
121               return 2;
122             }
123           if (!found[__DIR_CYGDRIVE])
124             {
125               found[__DIR_CYGDRIVE] = true;
126               if (cygdrive.Length > 0)
127                 {
128                   if (retname)
129                     *retname = cygdrive;
130                   return 2;
131                 }
132             }
133         }
134       return 0;
135     }
136     void rewind () { memset (found, 0, sizeof found); }
137 };
138
139 inline bool
140 path_conv::isgood_inode (__ino64_t ino) const
141 {
142   /* We can't trust remote inode numbers of only 32 bit.  That means,
143      remote NT4 NTFS, as well as shares of Samba version < 3.0.
144      The known exception are SFU NFS shares, which return the valid 32 bit
145      inode number from the remote file system unchanged. */
146   return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ());
147 }
148
149 /* Check reparse point for type.  IO_REPARSE_TAG_MOUNT_POINT types are
150    either volume mount points, which are treated as directories, or they
151    are directory mount points, which are treated as symlinks.
152    IO_REPARSE_TAG_SYMLINK types are always symlinks.  We don't know
153    anything about other reparse points, so they are treated as unknown.  */
154 static inline int
155 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr)
156 {
157   DWORD ret = DT_UNKNOWN;
158   IO_STATUS_BLOCK io;
159   HANDLE reph;
160   UNICODE_STRING subst;
161
162   if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, attr, &io,
163                               FILE_SHARE_VALID_FLAGS,
164                               FILE_OPEN_FOR_BACKUP_INTENT
165                               | FILE_OPEN_REPARSE_POINT)))
166     {
167       PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER)
168                   alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
169       if (NT_SUCCESS (NtFsControlFile (reph, NULL, NULL, NULL,
170                       &io, FSCTL_GET_REPARSE_POINT, NULL, 0,
171                       (LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE)))
172         {
173           if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
174             {
175               RtlInitCountedUnicodeString (&subst,
176                   (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
177                             + rp->MountPointReparseBuffer.SubstituteNameOffset),
178                   rp->MountPointReparseBuffer.SubstituteNameLength);
179               /* Only volume mountpoints are treated as directories. */
180               if (RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))
181                 ret = DT_DIR;
182               else
183                 ret = DT_LNK;
184             }
185           else if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
186             ret = DT_LNK;
187           NtClose (reph);
188         }
189     }
190   return ret;
191 }
192
193 inline __ino64_t
194 path_conv::get_ino_by_handle (HANDLE hdl)
195 {
196   IO_STATUS_BLOCK io;
197   FILE_INTERNAL_INFORMATION fai;
198
199   if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
200                                           FileInternalInformation))
201       && isgood_inode (fai.FileId.QuadPart))
202     return fai.FileId.QuadPart;
203   return 0;
204 }
205
206 #if 0
207 /* This function is obsolete.  We're keeping it in so we don't forget
208    that we already did all that at one point. */
209 unsigned __stdcall
210 path_conv::ndisk_links (DWORD nNumberOfLinks)
211 {
212   if (!isdir () || isremote ())
213     return nNumberOfLinks;
214
215   OBJECT_ATTRIBUTES attr;
216   IO_STATUS_BLOCK io;
217   HANDLE fh;
218
219   if (!NT_SUCCESS (NtOpenFile (&fh, SYNCHRONIZE | FILE_LIST_DIRECTORY,
220                                get_object_attr (attr, sec_none_nih),
221                                &io, FILE_SHARE_VALID_FLAGS,
222                                FILE_SYNCHRONOUS_IO_NONALERT
223                                | FILE_OPEN_FOR_BACKUP_INTENT
224                                | FILE_DIRECTORY_FILE)))
225     return nNumberOfLinks;
226
227   unsigned count = 0;
228   bool first = true;
229   PFILE_BOTH_DIRECTORY_INFORMATION fdibuf = (PFILE_BOTH_DIRECTORY_INFORMATION)
230                                        alloca (65536);
231   __DIR_mounts *dir = new __DIR_mounts (normalized_path);
232   while (NT_SUCCESS (NtQueryDirectoryFile (fh, NULL, NULL, NULL, &io, fdibuf,
233                                            65536, FileBothDirectoryInformation,
234                                            FALSE, NULL, first)))
235     {
236       if (first)
237         {
238           first = false;
239           /* All directories have . and .. as their first entries.
240              If . is not present as first entry, we're on a drive's
241              root direcotry, which doesn't have these entries. */
242           if (fdibuf->FileNameLength != 2 || fdibuf->FileName[0] != L'.')
243             count = 2;
244         }
245       for (PFILE_BOTH_DIRECTORY_INFORMATION pfdi = fdibuf;
246            pfdi;
247            pfdi = (PFILE_BOTH_DIRECTORY_INFORMATION)
248                   (pfdi->NextEntryOffset ? (PBYTE) pfdi + pfdi->NextEntryOffset
249                                          : NULL))
250         {
251           switch (pfdi->FileAttributes
252                   & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
253             {
254             case FILE_ATTRIBUTE_DIRECTORY:
255               /* Just a directory */
256               ++count;
257               break;
258             case FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT:
259               /* Volume mount point or symlink to directory */
260               {
261                 UNICODE_STRING fname;
262
263                 RtlInitCountedUnicodeString (&fname, pfdi->FileName,
264                                              pfdi->FileNameLength);
265                 InitializeObjectAttributes (&attr, &fname,
266                                             objcaseinsensitive (), fh, NULL);
267                 if (is_volume_mountpoint (&attr))
268                   ++count;
269               }
270               break;
271             default:
272               break;
273             }
274           UNICODE_STRING fname;
275           RtlInitCountedUnicodeString (&fname, pfdi->FileName,
276                                        pfdi->FileNameLength);
277           dir->check_mount (&fname, 0, false);
278         }
279     }
280   while (dir->check_missing_mount ())
281     ++count;
282   NtClose (fh);
283   delete dir;
284   return count;
285 }
286 #endif
287
288 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
289    This returns the content of a struct fattr3 as defined in RFC 1813.
290    The content is the NFS equivalent of struct stat. so there's not much
291    to do here except for copying. */
292 int __stdcall
293 fhandler_base::fstat_by_nfs_ea (struct __stat64 *buf)
294 {
295   fattr3 *nfs_attr = pc.nfsattr ();
296
297   if (get_io_handle ())
298     {
299       /* NFS stumbles over its own caching.  If you write to the file,
300          a subsequent fstat does not return the actual size of the file,
301          but the size at the time the handle has been opened.  Unless
302          access through another handle invalidates the caching within the
303          NFS client. */
304       if (get_access () & GENERIC_WRITE)
305         FlushFileBuffers (get_io_handle ());
306       nfs_fetch_fattr3 (get_io_handle (), nfs_attr);
307     }
308   buf->st_dev = nfs_attr->fsid;
309   buf->st_ino = nfs_attr->fileid;
310   buf->st_mode = (nfs_attr->mode & 0xfff)
311                  | nfs_type_mapping[nfs_attr->type & 7];
312   buf->st_nlink = nfs_attr->nlink;
313   /* FIXME: How to convert UNIX uid/gid to Windows SIDs? */
314 #if 0
315   buf->st_uid = nfs_attr->uid;
316   buf->st_gid = nfs_attr->gid;
317 #else
318   buf->st_uid = myself->uid;
319   buf->st_gid = myself->gid;
320 #endif
321   buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
322                           nfs_attr->rdev.specdata2);
323   buf->st_size = nfs_attr->size;
324   buf->st_blksize = PREFERRED_IO_BLKSIZE;
325   buf->st_blocks = (nfs_attr->used + S_BLKSIZE - 1) / S_BLKSIZE;
326   buf->st_atim = nfs_attr->atime;
327   buf->st_mtim = nfs_attr->mtime;
328   buf->st_ctim = nfs_attr->ctime;
329   return 0;
330 }
331
332 int __stdcall
333 fhandler_base::fstat_by_handle (struct __stat64 *buf)
334 {
335   /* Don't use FileAllInformation info class.  It returns a pathname rather
336      than a filename, so it needs a really big buffer for no good reason
337      since we don't need the name anyway.  So we just call the three info
338      classes necessary to get all information required by stat(2). */
339   FILE_STANDARD_INFORMATION fsi;
340   FILE_INTERNAL_INFORMATION fii;
341
342   HANDLE h = get_stat_handle ();
343   NTSTATUS status = 0;
344   IO_STATUS_BLOCK io;
345
346   /* If the file has been opened for other purposes than stat, we can't rely
347      on the information stored in pc.fnoi.  So we overwrite them here. */
348   if (get_io_handle ())
349     {
350       status = file_get_fnoi (h, pc.fs_is_netapp (), pc.fnoi ());
351       if (!NT_SUCCESS (status))
352        {
353          debug_printf ("%p = NtQueryInformationFile(%S, "
354                        "FileNetworkOpenInformation)",
355                        status, pc.get_nt_native_path ());
356          return -1;
357        }
358     }
359   if (!pc.hasgood_inode ())
360     fsi.NumberOfLinks = 1;
361   else
362     {
363       status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
364                                        FileStandardInformation);
365       if (!NT_SUCCESS (status))
366         {
367           debug_printf ("%p = NtQueryInformationFile(%S, "
368                         "FileStandardInformation)",
369                         status, pc.get_nt_native_path ());
370           return -1;
371         }
372       if (!ino)
373         {
374           status = NtQueryInformationFile (h, &io, &fii, sizeof fii,
375                                            FileInternalInformation);
376           if (!NT_SUCCESS (status))
377             {
378               debug_printf ("%p = NtQueryInformationFile(%S, "
379                             "FileInternalInformation)",
380                             status, pc.get_nt_native_path ());
381               return -1;
382             }
383           else if (pc.isgood_inode (fii.FileId.QuadPart))
384             ino = fii.FileId.QuadPart;
385         }
386     }
387   return fstat_helper (buf, fsi.NumberOfLinks);
388 }
389
390 int __stdcall
391 fhandler_base::fstat_by_name (struct __stat64 *buf)
392 {
393   NTSTATUS status;
394   OBJECT_ATTRIBUTES attr;
395   IO_STATUS_BLOCK io;
396   UNICODE_STRING dirname;
397   UNICODE_STRING basename;
398   HANDLE dir;
399   struct {
400     FILE_ID_BOTH_DIR_INFORMATION fdi;
401     WCHAR buf[NAME_MAX + 1];
402   } fdi_buf;
403
404   if (!ino && pc.hasgood_inode () && !pc.has_buggy_fileid_dirinfo ())
405     {
406       RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
407       InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
408                                   NULL, NULL);
409       status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
410                            &attr, &io, FILE_SHARE_VALID_FLAGS,
411                            FILE_SYNCHRONOUS_IO_NONALERT
412                            | FILE_OPEN_FOR_BACKUP_INTENT
413                            | FILE_DIRECTORY_FILE);
414       if (!NT_SUCCESS (status))
415         debug_printf ("%p = NtOpenFile(%S)", status,
416                       pc.get_nt_native_path ());
417       else
418         {
419           status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
420                                          &fdi_buf.fdi, sizeof fdi_buf,
421                                          FileIdBothDirectoryInformation,
422                                          TRUE, &basename, TRUE);
423           NtClose (dir);
424           if (!NT_SUCCESS (status))
425             debug_printf ("%p = NtQueryDirectoryFile(%S)", status,
426                           pc.get_nt_native_path ());
427           else
428             ino = fdi_buf.fdi.FileId.QuadPart;
429         }
430     }
431   return fstat_helper (buf, 1);
432 }
433
434 int __stdcall
435 fhandler_base::fstat_fs (struct __stat64 *buf)
436 {
437   int res = -1;
438   int oret;
439   int open_flags = O_RDONLY | O_BINARY;
440
441   if (get_stat_handle ())
442     {
443       if (!nohandle () && !is_fs_special ())
444         res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
445       if (res)
446         res = fstat_by_name (buf);
447       return res;
448     }
449   /* First try to open with generic read access.  This allows to read the file
450      in fstat_helper (when checking for executability) without having to
451      re-open it.  Opening a file can take a lot of time on network drives
452      so we try to avoid that. */
453   oret = open_fs (open_flags, 0);
454   if (!oret)
455     {
456       query_open (query_read_attributes);
457       oret = open_fs (open_flags, 0);
458     }
459   if (oret)
460     {
461       /* We now have a valid handle, regardless of the "nohandle" state.
462          Since fhandler_base::open only calls CloseHandle if !nohandle,
463          we have to set it to false before calling close and restore
464          the state afterwards. */
465       res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
466       bool no_handle = nohandle ();
467       nohandle (false);
468       close_fs ();
469       nohandle (no_handle);
470       set_io_handle (NULL);
471     }
472   if (res)
473     res = fstat_by_name (buf);
474
475   return res;
476 }
477
478 int __stdcall
479 fhandler_base::fstat_helper (struct __stat64 *buf,
480                              DWORD nNumberOfLinks)
481 {
482   IO_STATUS_BLOCK st;
483   FILE_COMPRESSION_INFORMATION fci;
484   HANDLE h = get_stat_handle ();
485   PFILE_NETWORK_OPEN_INFORMATION pfnoi = pc.fnoi ();
486   ULONG attributes = pc.file_attributes ();
487
488   to_timestruc_t ((PFILETIME) &pfnoi->LastAccessTime, &buf->st_atim);
489   to_timestruc_t ((PFILETIME) &pfnoi->LastWriteTime, &buf->st_mtim);
490   /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
491      (FAT for instance).  If so, it's faked using LastWriteTime. */
492   to_timestruc_t (pfnoi->ChangeTime.QuadPart ? (PFILETIME) &pfnoi->ChangeTime
493                                             : (PFILETIME) &pfnoi->LastWriteTime,
494                   &buf->st_ctim);
495   to_timestruc_t ((PFILETIME) &pfnoi->CreationTime, &buf->st_birthtim);
496   buf->st_rdev = buf->st_dev = get_dev ();
497   /* CV 2011-01-13: Observations on the Cygwin mailing list point to an
498      interesting behaviour in some Windows versions.  Apparently the size of
499      a directory is computed at the time the directory is first scanned.  This
500      can result in two subsequent NtQueryInformationFile calls to return size
501      0 in the first call and size > 0 in the second call.  This in turn can
502      affect applications like newer tar.
503      FIXME: Is the allocation size affected as well? */
504   buf->st_size = pc.isdir () ? 0 : (_off64_t) pfnoi->EndOfFile.QuadPart;
505   /* The number of links to a directory includes the number of subdirectories
506      in the directory, since all those subdirectories point to it.  However,
507      this is painfully slow, so we do without it. */
508 #if 0
509   buf->st_nlink = pc.ndisk_links (nNumberOfLinks);
510 #else
511   buf->st_nlink = nNumberOfLinks;
512 #endif
513
514   /* Enforce namehash as inode number on untrusted file systems. */
515   if (ino && pc.isgood_inode (ino))
516     buf->st_ino = (__ino64_t) ino;
517   else
518     buf->st_ino = get_ino ();
519
520   buf->st_blksize = PREFERRED_IO_BLKSIZE;
521
522   if (pfnoi->AllocationSize.QuadPart >= 0LL)
523     /* A successful NtQueryInformationFile returns the allocation size
524        correctly for compressed and sparse files as well. */
525     buf->st_blocks = (pfnoi->AllocationSize.QuadPart + S_BLKSIZE - 1)
526                      / S_BLKSIZE;
527   else if (::has_attribute (attributes, FILE_ATTRIBUTE_COMPRESSED
528                                         | FILE_ATTRIBUTE_SPARSE_FILE)
529            && h && !is_fs_special ()
530            && !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci,
531                                        FileCompressionInformation))
532     /* Otherwise we request the actual amount of bytes allocated for
533        compressed and sparsed files. */
534     buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
535                      / S_BLKSIZE;
536   else
537     /* Otherwise compute no. of blocks from file size. */
538     buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
539
540   buf->st_mode = 0;
541   /* Using a side effect: get_file_attributes checks for directory.
542      This is used, to set S_ISVTX, if needed.  */
543   if (pc.isdir ())
544     buf->st_mode = S_IFDIR;
545   else if (pc.issymlink ())
546     {
547       buf->st_size = pc.get_symlink_length ();
548       /* symlinks are everything for everyone! */
549       buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
550       get_file_attribute (h, pc, NULL,
551                           &buf->st_uid, &buf->st_gid);
552       goto done;
553     }
554   else if (pc.issocket ())
555     buf->st_mode = S_IFSOCK;
556
557   if (!get_file_attribute (is_fs_special () && !pc.issocket () ? NULL : h, pc,
558                            &buf->st_mode, &buf->st_uid, &buf->st_gid))
559     {
560       /* If read-only attribute is set, modify ntsec return value */
561       if (::has_attribute (attributes, FILE_ATTRIBUTE_READONLY)
562           && !pc.isdir () && !pc.issymlink ())
563         buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
564
565       if (buf->st_mode & S_IFMT)
566         /* nothing */;
567       else if (!is_fs_special ())
568         buf->st_mode |= S_IFREG;
569       else
570         {
571           buf->st_dev = buf->st_rdev = dev ();
572           buf->st_mode = dev ().mode;
573           buf->st_size = 0;
574         }
575     }
576   else
577     {
578       buf->st_mode |= STD_RBITS;
579
580       if (!::has_attribute (attributes, FILE_ATTRIBUTE_READONLY))
581         buf->st_mode |= STD_WBITS;
582       /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
583
584       if (pc.isdir ())
585         buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
586       else if (buf->st_mode & S_IFMT)
587         /* nothing */;
588       else if (is_fs_special ())
589         {
590           buf->st_dev = buf->st_rdev = dev ();
591           buf->st_mode = dev ().mode;
592           buf->st_size = 0;
593         }
594       else
595         {
596           buf->st_mode |= S_IFREG;
597           /* Check suffix for executable file. */
598           if (pc.exec_state () != is_executable)
599             {
600               PUNICODE_STRING path = pc.get_nt_native_path ();
601
602               if (RtlEqualUnicodePathSuffix (path, &ro_u_exe, TRUE)
603                   || RtlEqualUnicodePathSuffix (path, &ro_u_lnk, TRUE)
604                   || RtlEqualUnicodePathSuffix (path, &ro_u_com, TRUE))
605                 pc.set_exec ();
606             }
607           /* No known suffix, check file header.  This catches binaries and
608              shebang scripts. */
609           if (pc.exec_state () == dont_know_if_executable)
610             {
611               OBJECT_ATTRIBUTES attr;
612               NTSTATUS status = 0;
613               IO_STATUS_BLOCK io;
614
615               /* We have to re-open the file.  Either the file is not opened
616                  for reading, or the read will change the file position of the
617                  original handle. */
618               pc.init_reopen_attr (&attr, h);
619               status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA,
620                                    &attr, &io, FILE_SHARE_VALID_FLAGS,
621                                    FILE_OPEN_FOR_BACKUP_INTENT
622                                    | FILE_SYNCHRONOUS_IO_NONALERT);
623               if (!NT_SUCCESS (status))
624                 debug_printf ("%p = NtOpenFile(%S)", status,
625                               pc.get_nt_native_path ());
626               else
627                 {
628                   LARGE_INTEGER off = { QuadPart:0LL };
629                   char magic[3];
630
631                   status = NtReadFile (h, NULL, NULL, NULL,
632                                        &io, magic, 3, &off, NULL);
633                   if (!NT_SUCCESS (status))
634                     debug_printf ("%p = NtReadFile(%S)", status,
635                                   pc.get_nt_native_path ());
636                   else if (has_exec_chars (magic, io.Information))
637                     {
638                       /* Heureka, it's an executable */
639                       pc.set_exec ();
640                       buf->st_mode |= STD_XBITS;
641                     }
642                   NtClose (h);
643                 }
644             }
645         }
646       if (pc.exec_state () == is_executable)
647         buf->st_mode |= STD_XBITS;
648
649       /* This fakes the permissions of all files to match the current umask. */
650       buf->st_mode &= ~(cygheap->umask);
651       /* If the FS supports ACLs, we're here because we couldn't even open
652          the file for READ_CONTROL access.  Chances are high that the file's
653          security descriptor has no ACE for "Everyone", so we should not fake
654          any access for "others". */
655       if (has_acls ())
656         buf->st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH);
657     }
658
659  done:
660   syscall_printf ("0 = fstat (%S, %p) st_size=%D, st_mode=%p, st_ino=%D"
661                   "st_atim=%x.%x st_ctim=%x.%x "
662                   "st_mtim=%x.%x st_birthtim=%x.%x",
663                   pc.get_nt_native_path (), buf,
664                   buf->st_size, buf->st_mode, buf->st_ino,
665                   buf->st_atim.tv_sec, buf->st_atim.tv_nsec,
666                   buf->st_ctim.tv_sec, buf->st_ctim.tv_nsec,
667                   buf->st_mtim.tv_sec, buf->st_mtim.tv_nsec,
668                   buf->st_birthtim.tv_sec, buf->st_birthtim.tv_nsec);
669   return 0;
670 }
671
672 int __stdcall
673 fhandler_disk_file::fstat (struct __stat64 *buf)
674 {
675   return fstat_fs (buf);
676 }
677
678 int __stdcall
679 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
680 {
681   int ret = -1, opened = 0;
682   NTSTATUS status;
683   IO_STATUS_BLOCK io;
684   FILE_FS_FULL_SIZE_INFORMATION full_fsi;
685   FILE_FS_SIZE_INFORMATION fsi;
686   /* We must not use the stat handle here, even if it exists.  The handle
687      has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
688      mount point, it points to the FS of the mount point, rather than to the
689      mounted FS. */
690   HANDLE fh = get_handle ();
691
692   if (!fh)
693     {
694       OBJECT_ATTRIBUTES attr;
695       opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
696                                        pc.get_object_attr (attr, sec_none_nih),
697                                        &io, FILE_SHARE_VALID_FLAGS,
698                                        FILE_OPEN_FOR_BACKUP_INTENT));
699       if (!opened)
700         {
701           /* Can't open file.  Try again with parent dir. */
702           UNICODE_STRING dirname;
703           RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
704           attr.ObjectName = &dirname;
705           opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
706                                            FILE_SHARE_VALID_FLAGS,
707                                            FILE_OPEN_FOR_BACKUP_INTENT));
708           if (!opened)
709             goto out;
710         }
711     }
712
713   sfs->f_files = ULONG_MAX;
714   sfs->f_ffree = ULONG_MAX;
715   sfs->f_favail = ULONG_MAX;
716   sfs->f_fsid = pc.fs_serial_number ();
717   sfs->f_flag = pc.fs_flags ();
718   sfs->f_namemax = pc.fs_name_len ();
719   /* Get allocation related information.  Try to get "full" information
720      first, which is only available since W2K.  If that fails, try to
721      retrieve normal allocation information. */
722   status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
723                                          FileFsFullSizeInformation);
724   if (NT_SUCCESS (status))
725     {
726       sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
727       sfs->f_frsize = sfs->f_bsize;
728       sfs->f_blocks = full_fsi.TotalAllocationUnits.LowPart;
729       sfs->f_bfree = full_fsi.ActualAvailableAllocationUnits.LowPart;
730       sfs->f_bavail = full_fsi.CallerAvailableAllocationUnits.LowPart;
731       if (sfs->f_bfree > sfs->f_bavail)
732         {
733           /* Quotas active.  We can't trust TotalAllocationUnits. */
734           NTFS_VOLUME_DATA_BUFFER nvdb;
735
736           status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
737                                     FSCTL_GET_NTFS_VOLUME_DATA,
738                                     NULL, 0, &nvdb, sizeof nvdb);
739           if (!NT_SUCCESS (status))
740             debug_printf ("%p = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
741                           status, pc.get_nt_native_path ());
742           else
743             sfs->f_blocks = nvdb.TotalClusters.QuadPart;
744         }
745       ret = 0;
746     }
747   else
748     {
749       status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
750                                              FileFsSizeInformation);
751       if (!NT_SUCCESS (status))
752         {
753           __seterrno_from_nt_status (status);
754           goto out;
755         }
756       sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
757       sfs->f_frsize = sfs->f_bsize;
758       sfs->f_blocks = fsi.TotalAllocationUnits.LowPart;
759       sfs->f_bfree = fsi.AvailableAllocationUnits.LowPart;
760       sfs->f_bavail = sfs->f_bfree;
761       ret = 0;
762     }
763 out:
764   if (opened)
765     NtClose (fh);
766   syscall_printf ("%d = fstatvfs(%s, %p)", ret, get_name (), sfs);
767   return ret;
768 }
769
770 int __stdcall
771 fhandler_disk_file::fchmod (mode_t mode)
772 {
773   extern int chmod_device (path_conv& pc, mode_t mode);
774   int res = -1;
775   int oret = 0;
776   NTSTATUS status;
777   IO_STATUS_BLOCK io;
778
779   if (pc.is_fs_special ())
780     return chmod_device (pc, mode);
781
782   if (!get_handle ())
783     {
784       query_open (query_write_dac);
785       if (!(oret = open (O_BINARY, 0)))
786         {
787           /* Need WRITE_DAC|WRITE_OWNER to write ACLs. */
788           if (pc.has_acls ())
789             return -1;
790           /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
791           query_open (query_write_attributes);
792           if (!(oret = open (O_BINARY, 0)))
793             return -1;
794         }
795     }
796
797   if (pc.fs_is_nfs ())
798     {
799       /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
800          Only type and mode have to be set.  Apparently type isn't checked
801          for consistency, so it's sufficent to set it to NF3REG all the time. */
802       struct {
803         FILE_FULL_EA_INFORMATION ffei;
804         char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
805       } ffei_buf;
806       ffei_buf.ffei.NextEntryOffset = 0;
807       ffei_buf.ffei.Flags = 0;
808       ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
809       ffei_buf.ffei.EaValueLength = sizeof (fattr3);
810       strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
811       fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
812                                      + ffei_buf.ffei.EaNameLength + 1);
813       memset (nfs_attr, 0, sizeof (fattr3));
814       nfs_attr->type = NF3REG;
815       nfs_attr->mode = mode;
816       status = NtSetEaFile (get_handle (), &io,
817                             &ffei_buf.ffei, sizeof ffei_buf);
818       if (!NT_SUCCESS (status))
819         __seterrno_from_nt_status (status);
820       else
821         res = 0;
822       goto out;
823     }
824
825   if (pc.has_acls ())
826     {
827       if (pc.isdir ())
828         mode |= S_IFDIR;
829       if (!set_file_attribute (get_handle (), pc,
830                                ILLEGAL_UID, ILLEGAL_GID, mode))
831         res = 0;
832     }
833
834   /* If the mode has any write bits set, the DOS R/O flag is in the way. */
835   if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
836     pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
837   else if (!pc.has_acls ())     /* Never set DOS R/O if security is used. */
838     pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
839   if (S_ISSOCK (mode))
840     pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
841
842   status = NtSetAttributesFile (get_handle (), pc.file_attributes ());
843   /* MVFS needs a good amount of kicking to be convinced that it has to write
844      back metadata changes and to invalidate the cached metadata information
845      stored for the given handle.  This method to open a second handle to
846      the file and write the same metadata information twice has been found
847      experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
848   if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !oret)
849     {
850       OBJECT_ATTRIBUTES attr;
851       HANDLE fh;
852
853       pc.init_reopen_attr (&attr, get_handle ());
854       if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
855                                   FILE_SHARE_VALID_FLAGS,
856                                   FILE_OPEN_FOR_BACKUP_INTENT)))
857         {
858           NtSetAttributesFile (fh, pc.file_attributes ());
859           NtClose (fh);
860         }
861     }
862   /* Correct NTFS security attributes have higher priority */
863   if (!pc.has_acls ())
864     {
865       if (!NT_SUCCESS (status))
866         __seterrno_from_nt_status (status);
867       else
868         res = 0;
869     }
870
871 out:
872   if (oret)
873     close_fs ();
874
875   return res;
876 }
877
878 int __stdcall
879 fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
880 {
881   int oret = 0;
882
883   if (!pc.has_acls ())
884     {
885       /* fake - if not supported, pretend we're like win95
886          where it just works */
887       /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
888       return 0;
889     }
890
891   if (!get_handle ())
892     {
893       query_open (query_write_control);
894       if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
895         return -1;
896     }
897
898   mode_t attrib = 0;
899   if (pc.isdir ())
900     attrib |= S_IFDIR;
901   __uid32_t old_uid;
902   int res = get_file_attribute (get_handle (), pc, &attrib, &old_uid, NULL);
903   if (!res)
904     {
905       /* Typical Windows default ACLs can contain permissions for one
906          group, while being owned by another user/group.  The permission
907          bits returned above are pretty much useless then.  Creating a
908          new ACL with these useless permissions results in a potentially
909          broken symlink.  So what we do here is to set the underlying
910          permissions of symlinks to a sensible value which allows the
911          world to read the symlink and only the new owner to change it. */
912       if (pc.issymlink ())
913         attrib = S_IFLNK | STD_RBITS | STD_WBITS;
914       res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
915       /* If you're running a Samba server which has no winbidd running, the
916          uid<->SID mapping is disfunctional.  Even trying to chown to your
917          own account fails since the account used on the server is the UNIX
918          account which gets used for the standard user mapping.  This is a
919          default mechanism which doesn't know your real Windows SID.
920          There are two possible error codes in different Samba releases for
921          this situation, one of them is unfortunately the not very significant
922          STATUS_ACCESS_DENIED.  Instead of relying on the error codes, we're
923          using the below very simple heuristic.  If set_file_attribute failed,
924          and the original user account was either already unknown, or one of
925          the standard UNIX accounts, we're faking success. */
926       if (res == -1 && pc.fs_is_samba ())
927         {
928           cygsid sid;
929
930           if (old_uid == ILLEGAL_UID
931               || (sid.getfrompw (internal_getpwuid (old_uid))
932                   && RtlEqualPrefixSid (sid,
933                                         well_known_samba_unix_user_fake_sid)))
934             {
935               debug_printf ("Faking chown worked on standalone Samba");
936               res = 0;
937             }
938         }
939     }
940   if (oret)
941     close_fs ();
942
943   return res;
944 }
945
946 int _stdcall
947 fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp)
948 {
949   int res = -1;
950   int oret = 0;
951
952   if (!pc.has_acls ())
953     {
954 cant_access_acl:
955       switch (cmd)
956         {
957           struct __stat64 st;
958
959           case SETACL:
960             /* Open for writing required to be able to set ctime
961                (even though setting the ACL is just pretended). */
962             if (!get_handle ())
963               oret = open (O_WRONLY | O_BINARY, 0);
964             res = 0;
965             break;
966           case GETACL:
967             if (!aclbufp)
968               set_errno (EFAULT);
969             else if (nentries < MIN_ACL_ENTRIES)
970               set_errno (ENOSPC);
971             else
972               {
973                 if (!fstat (&st))
974                   {
975                     aclbufp[0].a_type = USER_OBJ;
976                     aclbufp[0].a_id = st.st_uid;
977                     aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
978                     aclbufp[1].a_type = GROUP_OBJ;
979                     aclbufp[1].a_id = st.st_gid;
980                     aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
981                     aclbufp[2].a_type = OTHER_OBJ;
982                     aclbufp[2].a_id = ILLEGAL_GID;
983                     aclbufp[2].a_perm = st.st_mode & S_IRWXO;
984                     aclbufp[3].a_type = CLASS_OBJ;
985                     aclbufp[3].a_id = ILLEGAL_GID;
986                     aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
987                     res = MIN_ACL_ENTRIES;
988                   }
989               }
990             break;
991           case GETACLCNT:
992             res = MIN_ACL_ENTRIES;
993             break;
994           default:
995             set_errno (EINVAL);
996             break;
997         }
998     }
999   else
1000     {
1001       if ((cmd == SETACL && !get_handle ())
1002           || (cmd != SETACL && !get_stat_handle ()))
1003         {
1004           query_open (cmd == SETACL ? query_write_control : query_read_control);
1005           if (!(oret = open (O_BINARY, 0)))
1006             {
1007               if (cmd == GETACL || cmd == GETACLCNT)
1008                 goto cant_access_acl;
1009               return -1;
1010             }
1011         }
1012       switch (cmd)
1013         {
1014           case SETACL:
1015             if (!aclsort32 (nentries, 0, aclbufp))
1016               {
1017                 bool rw = false;
1018                 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1019                 if (rw)
1020                   {
1021                     IO_STATUS_BLOCK io;
1022                     FILE_BASIC_INFORMATION fbi;
1023                     fbi.CreationTime.QuadPart
1024                     = fbi.LastAccessTime.QuadPart
1025                     = fbi.LastWriteTime.QuadPart
1026                     = fbi.ChangeTime.QuadPart = 0LL;
1027                     fbi.FileAttributes = (pc.file_attributes ()
1028                                           & ~FILE_ATTRIBUTE_READONLY)
1029                                          ?: FILE_ATTRIBUTE_NORMAL;
1030                     NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1031                                           FileBasicInformation);
1032                   }
1033               }
1034             break;
1035           case GETACL:
1036             if (!aclbufp)
1037               set_errno(EFAULT);
1038             else
1039               res = getacl (get_stat_handle (), pc, nentries, aclbufp);
1040               /* For this ENOSYS case, see security.cc:get_file_attribute(). */
1041               if (res == -1 && get_errno () == ENOSYS)
1042                 goto cant_access_acl;
1043             break;
1044           case GETACLCNT:
1045             res = getacl (get_stat_handle (), pc, 0, NULL);
1046             /* Ditto. */
1047             if (res == -1 && get_errno () == ENOSYS)
1048               goto cant_access_acl;
1049             break;
1050           default:
1051             set_errno (EINVAL);
1052             break;
1053         }
1054     }
1055
1056   if (oret)
1057     close_fs ();
1058
1059   return res;
1060 }
1061
1062 ssize_t
1063 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1064 {
1065   if (pc.is_fs_special ())
1066     {
1067       set_errno (ENOTSUP);
1068       return -1;
1069     }
1070   return read_ea (get_handle (), pc, name, (char *) value, size);
1071 }
1072
1073 int
1074 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1075                                int flags)
1076 {
1077   if (pc.is_fs_special ())
1078     {
1079       set_errno (ENOTSUP);
1080       return -1;
1081     }
1082   return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1083 }
1084
1085 int
1086 fhandler_disk_file::fadvise (_off64_t offset, _off64_t length, int advice)
1087 {
1088   if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1089     {
1090       set_errno (EINVAL);
1091       return -1;
1092     }
1093
1094   /* Windows only supports advice flags for the whole file.  We're using
1095      a simplified test here so that we don't have to ask for the actual
1096      file size.  Length == 0 means all bytes starting at offset anyway.
1097      So we only actually follow the advice, if it's given for offset == 0. */
1098   if (offset != 0)
1099     return 0;
1100
1101   /* We only support normal and sequential mode for now.  Everything which
1102      is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1103   if (advice != POSIX_FADV_SEQUENTIAL)
1104     advice = POSIX_FADV_NORMAL;
1105
1106   IO_STATUS_BLOCK io;
1107   FILE_MODE_INFORMATION fmi;
1108   NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1109                                             &fmi, sizeof fmi,
1110                                             FileModeInformation);
1111   if (!NT_SUCCESS (status))
1112     __seterrno_from_nt_status (status);
1113   else
1114     {
1115       fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1116       if (advice == POSIX_FADV_SEQUENTIAL)
1117         fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1118       status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1119                                      FileModeInformation);
1120       if (NT_SUCCESS (status))
1121         return 0;
1122       __seterrno_from_nt_status (status);
1123     }
1124
1125   return -1;
1126 }
1127
1128 int
1129 fhandler_disk_file::ftruncate (_off64_t length, bool allow_truncate)
1130 {
1131   int res = -1;
1132
1133   if (length < 0 || !get_handle ())
1134     set_errno (EINVAL);
1135   else if (pc.isdir ())
1136     set_errno (EISDIR);
1137   else if (!(get_access () & GENERIC_WRITE))
1138     set_errno (EBADF);
1139   else
1140     {
1141       NTSTATUS status;
1142       IO_STATUS_BLOCK io;
1143       FILE_STANDARD_INFORMATION fsi;
1144       FILE_END_OF_FILE_INFORMATION feofi;
1145
1146       status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1147                                        FileStandardInformation);
1148       if (!NT_SUCCESS (status))
1149         {
1150           __seterrno_from_nt_status (status);
1151           return -1;
1152         }
1153
1154       /* If called through posix_fallocate, silently succeed if length
1155          is less than the file's actual length. */
1156       if (!allow_truncate && length < fsi.EndOfFile.QuadPart)
1157         return 0;
1158
1159       feofi.EndOfFile.QuadPart = length;
1160       /* Create sparse files only when called through ftruncate, not when
1161          called through posix_fallocate. */
1162       if (allow_truncate
1163           && (pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES)
1164           && length >= fsi.EndOfFile.QuadPart + (128 * 1024))
1165         {
1166           status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1167                                     FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1168           syscall_printf ("%p = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1169                           status, pc.get_nt_native_path ());
1170         }
1171       status = NtSetInformationFile (get_handle (), &io,
1172                                      &feofi, sizeof feofi,
1173                                      FileEndOfFileInformation);
1174       if (!NT_SUCCESS (status))
1175         __seterrno_from_nt_status (status);
1176       else
1177         res = 0;
1178     }
1179   return res;
1180 }
1181
1182 int
1183 fhandler_disk_file::link (const char *newpath)
1184 {
1185   size_t nlen = strlen (newpath);
1186   path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
1187   if (newpc.error)
1188     {
1189       set_errno (newpc.error);
1190       return -1;
1191     }
1192
1193   if (newpc.exists ())
1194     {
1195       syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1196       set_errno (EEXIST);
1197       return -1;
1198     }
1199
1200   if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
1201     {
1202       set_errno (ENOENT);
1203       return -1;
1204     }
1205
1206   char new_buf[nlen + 5];
1207   if (!newpc.error)
1208     {
1209       /* If the original file is a lnk special file (except for sockets),
1210          and if the original file has a .lnk suffix, add one to the hardlink
1211          as well. */
1212       if (pc.is_lnk_special () && !pc.issocket ()
1213           && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1214                                         &ro_u_lnk, TRUE))
1215         {
1216           /* Shortcut hack. */
1217           stpcpy (stpcpy (new_buf, newpath), ".lnk");
1218           newpath = new_buf;
1219           newpc.check (newpath, PC_SYM_NOFOLLOW);
1220         }
1221       else if (!pc.isdir ()
1222                && pc.is_binary ()
1223                && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1224                                              &ro_u_exe, TRUE)
1225                && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1226                                               &ro_u_exe, TRUE))
1227         {
1228           /* Executable hack. */
1229           stpcpy (stpcpy (new_buf, newpath), ".exe");
1230           newpath = new_buf;
1231           newpc.check (newpath, PC_SYM_NOFOLLOW);
1232         }
1233     }
1234
1235   /* We only need READ_CONTROL access so the handle returned in pc is
1236      sufficient.  And if the file couldn't be opened with READ_CONTROL
1237      access in path_conv, we won't be able to do it here anyway. */
1238   HANDLE fh = get_stat_handle ();
1239   if (!fh)
1240     {
1241       set_errno (EACCES);
1242       return -1;
1243     }
1244   PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1245   ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1246   PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1247   pfli->ReplaceIfExists = FALSE;
1248   pfli->RootDirectory = NULL;
1249   memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1250
1251   NTSTATUS status;
1252   IO_STATUS_BLOCK io;
1253   status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1254   if (!NT_SUCCESS (status))
1255     {
1256       if (status == STATUS_INVALID_DEVICE_REQUEST)
1257         {
1258           /* FS doesn't support hard links.  Linux returns EPERM. */
1259           set_errno (EPERM);
1260           return -1;
1261         }
1262       else
1263         {
1264           __seterrno_from_nt_status (status);
1265           return -1;
1266         }
1267     }
1268   return 0;
1269 }
1270
1271 int
1272 fhandler_disk_file::utimens (const struct timespec *tvp)
1273 {
1274   return utimens_fs (tvp);
1275 }
1276
1277 int
1278 fhandler_base::utimens_fs (const struct timespec *tvp)
1279 {
1280   struct timespec timeofday;
1281   struct timespec tmp[2];
1282   bool closeit = false;
1283
1284   if (!get_handle ())
1285     {
1286       query_open (query_write_attributes);
1287       if (!open_fs (O_BINARY, 0))
1288         {
1289           /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1290              to change the timestamps.  Unfortunately it's not sufficient for a
1291              remote HPFS which requires GENERIC_WRITE, so we just retry to open
1292              for writing, though this fails for R/O files of course. */
1293           query_open (no_query);
1294           if (!open_fs (O_WRONLY | O_BINARY, 0))
1295             {
1296               syscall_printf ("Opening file failed");
1297               return -1;
1298             }
1299         }
1300       closeit = true;
1301     }
1302
1303   clock_gettime (CLOCK_REALTIME, &timeofday);
1304   if (!tvp)
1305     tmp[1] = tmp[0] = timeofday;
1306   else
1307     {
1308       if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1309           || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1310         {
1311           if (closeit)
1312             close_fs ();
1313           set_errno (EINVAL);
1314           return -1;
1315         }
1316       tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1317       tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1318     }
1319   debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
1320
1321   IO_STATUS_BLOCK io;
1322   FILE_BASIC_INFORMATION fbi;
1323
1324   fbi.CreationTime.QuadPart = 0LL;
1325   /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1326   timespec_to_filetime (&tmp[0], (LPFILETIME) &fbi.LastAccessTime);
1327   timespec_to_filetime (&tmp[1], (LPFILETIME) &fbi.LastWriteTime);
1328   fbi.ChangeTime.QuadPart = 0LL;
1329   fbi.FileAttributes = 0;
1330   NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1331                                           FileBasicInformation);
1332   /* For this special case for MVFS see the comment in
1333      fhandler_disk_file::fchmod. */
1334   if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
1335     {
1336       OBJECT_ATTRIBUTES attr;
1337       HANDLE fh;
1338
1339       pc.init_reopen_attr (&attr, get_handle ());
1340       if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
1341                                   FILE_SHARE_VALID_FLAGS,
1342                                   FILE_OPEN_FOR_BACKUP_INTENT)))
1343         {
1344           NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1345                                 FileBasicInformation);
1346           NtClose (fh);
1347         }
1348     }
1349   if (closeit)
1350     close_fs ();
1351   /* Opening a directory on a 9x share from a NT machine works(!), but
1352      then NtSetInformationFile fails with STATUS_NOT_SUPPORTED.  Oh well... */
1353   if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1354     {
1355       __seterrno_from_nt_status (status);
1356       return -1;
1357     }
1358   return 0;
1359 }
1360
1361 fhandler_disk_file::fhandler_disk_file () :
1362   fhandler_base (), prw_handle (NULL)
1363 {
1364 }
1365
1366 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1367   fhandler_base (), prw_handle (NULL)
1368 {
1369   set_name (pc);
1370 }
1371
1372 int
1373 fhandler_disk_file::open (int flags, mode_t mode)
1374 {
1375   return open_fs (flags, mode);
1376 }
1377
1378 int
1379 fhandler_disk_file::close ()
1380 {
1381   /* Close extra pread/pwrite handle, if it exists. */
1382   if (prw_handle)
1383     NtClose (prw_handle);
1384   /* Delete all POSIX locks on the file.  Delete all flock locks on the
1385      file if this is the last reference to this file. */
1386   del_my_locks (on_close);
1387   return fhandler_base::close ();
1388 }
1389
1390 int
1391 fhandler_disk_file::dup (fhandler_base *child, int flags)
1392 {
1393   fhandler_disk_file *fhc = (fhandler_disk_file *) child;
1394
1395   int ret = fhandler_base::dup (child, flags);
1396   if (!ret && prw_handle
1397       && !DuplicateHandle (GetCurrentProcess (), prw_handle,
1398                            GetCurrentProcess (), &fhc->prw_handle,
1399                            0, TRUE, DUPLICATE_SAME_ACCESS))
1400     prw_handle = NULL;
1401   return ret;
1402 }
1403
1404 void
1405 fhandler_disk_file::fixup_after_fork (HANDLE parent)
1406 {
1407   prw_handle = NULL;
1408   fhandler_base::fixup_after_fork (parent);
1409 }
1410
1411 int
1412 fhandler_base::open_fs (int flags, mode_t mode)
1413 {
1414   /* Unfortunately NT allows to open directories for writing, but that's
1415      disallowed according to SUSv3. */
1416   if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1417     {
1418       set_errno (EISDIR);
1419       return 0;
1420     }
1421
1422   int res = fhandler_base::open (flags | O_DIROPEN, mode);
1423   if (!res)
1424     goto out;
1425
1426   /* This is for file systems known for having a buggy CreateFile call
1427      which might return a valid HANDLE without having actually opened
1428      the file.
1429      The only known file system to date is the SUN NFS Solstice Client 3.1
1430      which returns a valid handle when trying to open a file in a nonexistent
1431      directory. */
1432   if (pc.has_buggy_open () && !pc.exists ())
1433     {
1434       debug_printf ("Buggy open detected.");
1435       close_fs ();
1436       set_errno (ENOENT);
1437       return 0;
1438     }
1439
1440     ino = pc.get_ino_by_handle (get_handle ());
1441     /* A unique ID is necessary to recognize fhandler entries which are
1442        duplicated by dup(2) or fork(2). */
1443     NtAllocateLocallyUniqueId ((PLUID) &unique_id);
1444
1445 out:
1446   syscall_printf ("%d = fhandler_disk_file::open(%S, %p)", res,
1447                   pc.get_nt_native_path (), flags);
1448   return res;
1449 }
1450
1451 /* POSIX demands that pread/pwrite don't change the current file position.
1452    While NtReadFile/NtWriteFile support atomic seek-and-io, both change the
1453    file pointer if the file handle has been opened for synchonous I/O.
1454    Using this handle for pread/pwrite would break atomicity, because the
1455    read/write operation would have to be followed by a seek back to the old
1456    file position.  What we do is to open another handle to the file on the
1457    first call to either pread or pwrite.  This is used for any subsequent
1458    pread/pwrite.  Thus the current file position of the "normal" file
1459    handle is not touched.
1460
1461    FIXME:
1462
1463    Note that this is just a hack.  The problem with this approach is that
1464    a change to the file permissions might disallow to open the file with
1465    the required permissions to read or write.  This appears to be a border case,
1466    but that's exactly what git does.  It creates the file for reading and
1467    writing and after writing it, it chmods the file to read-only.  Then it
1468    calls pread on the file to examine the content.  This works, but if git
1469    would use the original handle to pwrite to the file, it would be broken
1470    with our approach.
1471
1472    One way to implement this is to open the pread/pwrite handle right at
1473    file open time.  We would simply maintain two handles, which wouldn't
1474    be much of a problem given how we do that for other fhandler types as
1475    well.
1476
1477    However, ultimately fhandler_disk_file should become a derived class of
1478    fhandler_base_overlapped.  Each raw_read or raw_write would fetch the
1479    actual file position, read/write from there, and then set the file
1480    position again.  Fortunately, while the file position is not maintained
1481    by the I/O manager, it can be fetched and set to a new value by all
1482    processes holding a handle to that file object.  Pread and pwrite differ
1483    from raw_read and raw_write just by not touching the current file pos.
1484    Actually they could be merged with raw_read/raw_write if we add a position
1485    parameter to the latter. */
1486
1487 int
1488 fhandler_disk_file::prw_open (bool write)
1489 {
1490   NTSTATUS status;
1491   IO_STATUS_BLOCK io;
1492   OBJECT_ATTRIBUTES attr;
1493
1494   /* First try to open with the original access mask */
1495   ACCESS_MASK access = get_access ();
1496   pc.init_reopen_attr (&attr, get_handle ());
1497   status = NtOpenFile (&prw_handle, access, &attr, &io,
1498                        FILE_SHARE_VALID_FLAGS, get_options ());
1499   if (status == STATUS_ACCESS_DENIED)
1500     {
1501       /* If we get an access denied, chmod has been called.  Try again
1502          with just the required rights to perform the called function. */
1503       access &= write ? ~GENERIC_READ : ~GENERIC_WRITE;
1504       status = NtOpenFile (&prw_handle, access, &attr, &io,
1505                            FILE_SHARE_VALID_FLAGS, get_options ());
1506     }
1507   debug_printf ("%x = NtOpenFile (%p, %x, %S, io, %x, %x)",
1508                 status, prw_handle, access, pc.get_nt_native_path (),
1509                 FILE_SHARE_VALID_FLAGS, get_options ());
1510   if (!NT_SUCCESS (status))
1511     {
1512       __seterrno_from_nt_status (status);
1513       return -1;
1514     }
1515   return 0;
1516 }
1517
1518 ssize_t __stdcall
1519 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
1520 {
1521   if ((get_flags () & O_ACCMODE) == O_WRONLY)
1522     {
1523       set_errno (EBADF);
1524       return -1;
1525     }
1526
1527   /* In binary mode, we can use an atomic NtReadFile call. */
1528   if (rbinary ())
1529     {
1530       extern int __stdcall is_at_eof (HANDLE h);
1531       NTSTATUS status;
1532       IO_STATUS_BLOCK io;
1533       LARGE_INTEGER off = { QuadPart:offset };
1534
1535       if (!prw_handle && prw_open (false))
1536         goto non_atomic;
1537       status = NtReadFile (prw_handle, NULL, NULL, NULL, &io, buf, count,
1538                            &off, NULL);
1539       if (!NT_SUCCESS (status))
1540         {
1541           if (pc.isdir ())
1542             {
1543               set_errno (EISDIR);
1544               return -1;
1545             }
1546           if (status == (NTSTATUS) STATUS_ACCESS_VIOLATION)
1547             {
1548               if (is_at_eof (prw_handle))
1549                 return 0;
1550               switch (mmap_is_attached_or_noreserve (buf, count))
1551                 {
1552                 case MMAP_NORESERVE_COMMITED:
1553                   status = NtReadFile (prw_handle, NULL, NULL, NULL, &io,
1554                                        buf, count, &off, NULL);
1555                   if (NT_SUCCESS (status))
1556                     return io.Information;
1557                   break;
1558                 case MMAP_RAISE_SIGBUS:
1559                   raise (SIGBUS);
1560                 default:
1561                   break;
1562                 }
1563             }
1564           __seterrno_from_nt_status (status);
1565           return -1;
1566         }
1567     }
1568
1569 non_atomic:
1570   /* Text mode stays slow and non-atomic. */
1571   ssize_t res;
1572   _off64_t curpos = lseek (0, SEEK_CUR);
1573   if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1574     res = -1;
1575   else
1576     {
1577       size_t tmp_count = count;
1578       read (buf, tmp_count);
1579       if (lseek (curpos, SEEK_SET) >= 0)
1580         res = (ssize_t) tmp_count;
1581       else
1582         res = -1;
1583     }
1584   debug_printf ("%d = pread(%p, %d, %d)\n", res, buf, count, offset);
1585   return res;
1586 }
1587
1588 ssize_t __stdcall
1589 fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
1590 {
1591   if ((get_flags () & O_ACCMODE) == O_RDONLY)
1592     {
1593       set_errno (EBADF);
1594       return -1;
1595     }
1596
1597   /* In binary mode, we can use an atomic NtWriteFile call. */
1598   if (wbinary ())
1599     {
1600       NTSTATUS status;
1601       IO_STATUS_BLOCK io;
1602       LARGE_INTEGER off = { QuadPart:offset };
1603
1604       if (!prw_handle && prw_open (true))
1605         goto non_atomic;
1606       status = NtWriteFile (prw_handle, NULL, NULL, NULL, &io, buf, count,
1607                             &off, NULL);
1608       if (!NT_SUCCESS (status))
1609         {
1610           __seterrno_from_nt_status (status);
1611           return -1;
1612         }
1613       return io.Information;
1614     }
1615
1616 non_atomic:
1617   /* Text mode stays slow and non-atomic. */
1618   int res;
1619   _off64_t curpos = lseek (0, SEEK_CUR);
1620   if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1621     res = curpos;
1622   else
1623     {
1624       res = (ssize_t) write (buf, count);
1625       if (lseek (curpos, SEEK_SET) < 0)
1626         res = -1;
1627     }
1628   debug_printf ("%d = pwrite(%p, %d, %d)\n", res, buf, count, offset);
1629   return res;
1630 }
1631
1632 int
1633 fhandler_disk_file::mkdir (mode_t mode)
1634 {
1635   int res = -1;
1636   SECURITY_ATTRIBUTES sa = sec_none_nih;
1637   NTSTATUS status;
1638   HANDLE dir;
1639   OBJECT_ATTRIBUTES attr;
1640   IO_STATUS_BLOCK io;
1641   PFILE_FULL_EA_INFORMATION p = NULL;
1642   ULONG plen = 0;
1643   ULONG access = FILE_LIST_DIRECTORY | SYNCHRONIZE;
1644
1645   if (pc.fs_is_nfs ())
1646     {
1647       /* When creating a dir on an NFS share, we have to set the
1648          file mode by writing a NFS fattr3 structure with the
1649          correct mode bits set. */
1650       plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1651              + sizeof (fattr3);
1652       p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1653       p->NextEntryOffset = 0;
1654       p->Flags = 0;
1655       p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1656       p->EaValueLength = sizeof (fattr3);
1657       strcpy (p->EaName, NFS_V3_ATTR);
1658       fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1659       memset (nfs_attr, 0, sizeof (fattr3));
1660       nfs_attr->type = NF3DIR;
1661       nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1662     }
1663   else if (has_acls () && !isremote ())
1664     /* If the filesystem supports ACLs, we will overwrite the DACL after the
1665        call to NtCreateFile.  This requires a handle with READ_CONTROL and
1666        WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
1667        open the file again.
1668        FIXME: On remote NTFS shares open sometimes fails because even the
1669        creator of the file doesn't have the right to change the DACL.
1670        I don't know what setting that is or how to recognize such a share,
1671        so for now we don't request WRITE_DAC on remote drives. */
1672     access |= READ_CONTROL | WRITE_DAC;
1673   status = NtCreateFile (&dir, access, pc.get_object_attr (attr, sa), &io, NULL,
1674                          FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
1675                          FILE_CREATE,
1676                          FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1677                          | FILE_OPEN_FOR_BACKUP_INTENT,
1678                          p, plen);
1679   if (NT_SUCCESS (status))
1680     {
1681       if (has_acls ())
1682         set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
1683                             S_JUSTCREATED | S_IFDIR
1684                             | ((mode & 07777) & ~cygheap->umask));
1685       NtClose (dir);
1686       res = 0;
1687     }
1688   else
1689     __seterrno_from_nt_status (status);
1690
1691   return res;
1692 }
1693
1694 int
1695 fhandler_disk_file::rmdir ()
1696 {
1697   extern NTSTATUS unlink_nt (path_conv &pc);
1698
1699   if (!pc.isdir ())
1700     {
1701       set_errno (ENOTDIR);
1702       return -1;
1703     }
1704   if (!pc.exists ())
1705     {
1706       set_errno (ENOENT);
1707       return -1;
1708     }
1709
1710   NTSTATUS status = unlink_nt (pc);
1711
1712   /* Check for existence of remote dirs after trying to delete them.
1713      Two reasons:
1714      - Sometimes SMB indicates failure when it really succeeds.
1715      - Removing a directory on a Samba drive using an old Samba version
1716        sometimes doesn't return an error, if the directory can't be removed
1717        because it's not empty. */
1718   if (isremote ())
1719     {
1720       OBJECT_ATTRIBUTES attr;
1721       FILE_BASIC_INFORMATION fbi;
1722       NTSTATUS q_status;
1723
1724       q_status = NtQueryAttributesFile (pc.get_object_attr (attr, sec_none_nih),
1725                                         &fbi);
1726       if (!NT_SUCCESS (status) && q_status == STATUS_OBJECT_NAME_NOT_FOUND)
1727         status = STATUS_SUCCESS;
1728       else if (pc.fs_is_samba ()
1729                && NT_SUCCESS (status) && NT_SUCCESS (q_status))
1730         status = STATUS_DIRECTORY_NOT_EMPTY;
1731     }
1732   if (!NT_SUCCESS (status))
1733     {
1734       __seterrno_from_nt_status (status);
1735       return -1;
1736     }
1737   return 0;
1738 }
1739
1740 /* This is the minimal number of entries which fit into the readdir cache.
1741    The number of bytes allocated by the cache is determined by this number,
1742    To tune caching, just tweak this number.  To get a feeling for the size,
1743    the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes.  */
1744
1745 #define DIR_NUM_ENTRIES 100             /* Cache size 62404 bytes */
1746
1747 #define DIR_BUF_SIZE    (DIR_NUM_ENTRIES \
1748                          * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1749                             + (NAME_MAX + 1) * sizeof (WCHAR)))
1750
1751 struct __DIR_cache
1752 {
1753   char  __cache[DIR_BUF_SIZE];  /* W2K needs this buffer 8 byte aligned. */
1754   ULONG __pos;
1755 };
1756
1757 #define d_cachepos(d)   (((__DIR_cache *) (d)->__d_dirname)->__pos)
1758 #define d_cache(d)      (((__DIR_cache *) (d)->__d_dirname)->__cache)
1759
1760 #define d_mounts(d)     ((__DIR_mounts *) (d)->__d_internal)
1761
1762 DIR *
1763 fhandler_disk_file::opendir (int fd)
1764 {
1765   DIR *dir;
1766   DIR *res = NULL;
1767
1768   if (!pc.isdir ())
1769     set_errno (ENOTDIR);
1770   else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1771     set_errno (ENOMEM);
1772   else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1773            == NULL)
1774     {
1775       set_errno (ENOMEM);
1776       goto free_dir;
1777     }
1778   else if ((dir->__d_dirent =
1779             (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1780     {
1781       set_errno (ENOMEM);
1782       goto free_dirname;
1783     }
1784   else
1785     {
1786       cygheap_fdnew cfd;
1787       if (cfd < 0 && fd < 0)
1788         goto free_dirent;
1789
1790       dir->__d_dirent->__d_version = __DIRENT_VERSION;
1791       dir->__d_cookie = __DIRENT_COOKIE;
1792       dir->__handle = INVALID_HANDLE_VALUE;
1793       dir->__d_position = 0;
1794       dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1795                      ? dirent_isroot : 0;
1796       dir->__d_internal = (unsigned) new __DIR_mounts (get_name ());
1797       d_cachepos (dir) = 0;
1798
1799       if (!pc.iscygdrive ())
1800         {
1801           if (fd < 0)
1802             {
1803               /* opendir() case.  Initialize with given directory name and
1804                  NULL directory handle. */
1805               OBJECT_ATTRIBUTES attr;
1806               NTSTATUS status;
1807               IO_STATUS_BLOCK io;
1808               /* Tools like ls(1) call dirfd() to fetch the directory
1809                  descriptor for calls to facl or fstat.  The tight access mask
1810                  used so far is not sufficient to reuse the handle for these
1811                  calls, instead the facl/fstat calls find the handle to be
1812                  unusable and have to re-open the file for reading attributes
1813                  and control data.  So, what we do here is to try to open the
1814                  directory with more relaxed access mask which enables to use
1815                  the handle for the aforementioned purpose.  This should work
1816                  in almost all cases.  Only if it doesn't work due to
1817                  permission problems, we drop the additional access bits and
1818                  try again. */
1819               ACCESS_MASK fstat_mask = READ_CONTROL | FILE_READ_ATTRIBUTES;
1820
1821               do
1822                 {
1823                   status = NtOpenFile (&get_handle (),
1824                                        SYNCHRONIZE | FILE_LIST_DIRECTORY
1825                                        | fstat_mask,
1826                                        pc.get_object_attr (attr, sec_none_nih),
1827                                        &io, FILE_SHARE_VALID_FLAGS,
1828                                        FILE_SYNCHRONOUS_IO_NONALERT
1829                                        | FILE_OPEN_FOR_BACKUP_INTENT
1830                                        | FILE_DIRECTORY_FILE);
1831                   if (!NT_SUCCESS (status))
1832                     {
1833                       if (status == STATUS_ACCESS_DENIED && fstat_mask)
1834                         fstat_mask = 0;
1835                       else
1836                         {
1837                           __seterrno_from_nt_status (status);
1838                           goto free_mounts;
1839                         }
1840                     }
1841                 }
1842               while (!NT_SUCCESS (status));
1843             }
1844
1845           /* FileIdBothDirectoryInformation is apparently unsupported on
1846              XP when accessing directories on UDF.  When trying to use it
1847              so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1848              It's not clear if the call isn't also unsupported on other
1849              OS/FS combinations (say, Win2K/CDFS or so).  Instead of
1850              testing in readdir for yet another error code, let's use
1851              FileIdBothDirectoryInformation only on filesystems supporting
1852              persistent ACLs, FileBothDirectoryInformation otherwise.
1853
1854              NFS clients hide dangling symlinks from directory queries,
1855              unless you use the FileNamesInformation info class.
1856              On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
1857              works fine, but only if the NFS share is mounted to a drive
1858              letter.  TODO: We don't test that here for now, but it might
1859              be worth to test if there's a speed gain in using
1860              FileIdBothDirectoryInformation, because it doesn't require to
1861              open the file to read the inode number. */
1862           if (pc.hasgood_inode ())
1863             {
1864               dir->__flags |= dirent_set_d_ino;
1865               if (pc.fs_is_nfs ())
1866                 dir->__flags |= dirent_nfs_d_ino;
1867               else if (!pc.has_buggy_fileid_dirinfo ())
1868                 dir->__flags |= dirent_get_d_ino;
1869             }
1870         }
1871       if (fd >= 0)
1872         dir->__d_fd = fd;
1873       else
1874         {
1875           /* Filling cfd with `this' (aka storing this in the file
1876              descriptor table should only happen after it's clear that
1877              opendir doesn't fail, otherwise we end up cfree'ing the
1878              fhandler twice, once in opendir() in dir.cc, the second
1879              time on exit.  Nasty, nasty... */
1880           cfd = this;
1881           dir->__d_fd = cfd;
1882           if (pc.iscygdrive ())
1883             cfd->nohandle (true);
1884         }
1885       set_close_on_exec (true);
1886       dir->__fh = this;
1887       res = dir;
1888     }
1889
1890   syscall_printf ("%p = opendir (%s)", res, get_name ());
1891   return res;
1892
1893 free_mounts:
1894   delete d_mounts (dir);
1895 free_dirent:
1896   free (dir->__d_dirent);
1897 free_dirname:
1898   free (dir->__d_dirname);
1899 free_dir:
1900   free (dir);
1901   return res;
1902 }
1903
1904 __ino64_t __stdcall
1905 readdir_get_ino (const char *path, bool dot_dot)
1906 {
1907   char *fname;
1908   struct __stat64 st;
1909   HANDLE hdl;
1910   OBJECT_ATTRIBUTES attr;
1911   IO_STATUS_BLOCK io;
1912   __ino64_t ino = 0;
1913
1914   if (dot_dot)
1915     {
1916       fname = (char *) alloca (strlen (path) + 4);
1917       char *c = stpcpy (fname, path);
1918       if (c[-1] != '/')
1919         *c++ = '/';
1920       strcpy (c, "..");
1921       path = fname;
1922     }
1923   path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN | PC_KEEP_HANDLE);
1924   if (pc.isspecial ())
1925     {
1926       if (!stat_worker (pc, &st))
1927         ino = st.st_ino;
1928     }
1929   else if (!pc.hasgood_inode ())
1930     ino = hash_path_name (0, pc.get_nt_native_path ());
1931   else if ((hdl = pc.handle ()) != NULL
1932            || NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1933                                       pc.get_object_attr (attr, sec_none_nih),
1934                                       &io, FILE_SHARE_VALID_FLAGS,
1935                                       FILE_OPEN_FOR_BACKUP_INTENT
1936                                       | (pc.is_rep_symlink ()
1937                                       ? FILE_OPEN_REPARSE_POINT : 0)))
1938           )
1939     {
1940       ino = pc.get_ino_by_handle (hdl);
1941       if (!ino)
1942         ino = hash_path_name (0, pc.get_nt_native_path ());
1943     }
1944   return ino;
1945 }
1946
1947 int
1948 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1949                                     DWORD attr, PUNICODE_STRING fname)
1950 {
1951   if (w32_err)
1952     {
1953       bool added = false;
1954       if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
1955         added = true;
1956       if (!added)
1957         {
1958           fname->Length = 0;
1959           return geterrno_from_win_error (w32_err);
1960         }
1961
1962       attr = 0;
1963       dir->__flags &= ~dirent_set_d_ino;
1964     }
1965
1966   /* Set d_type if type can be determined from file attributes.  For .lnk
1967      symlinks, d_type will be reset below.  Reparse points can be NTFS
1968      symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
1969   if (attr &&
1970       !(attr & (~FILE_ATTRIBUTE_VALID_FLAGS | FILE_ATTRIBUTE_REPARSE_POINT)))
1971     {
1972       if (attr & FILE_ATTRIBUTE_DIRECTORY)
1973         de->d_type = DT_DIR;
1974       /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
1975       else if (!(attr & FILE_ATTRIBUTE_SYSTEM))
1976         de->d_type = DT_REG;
1977     }
1978
1979   /* Check for directory reparse point.  These are potential volume mount
1980      points which have another inode than the underlying directory. */
1981   if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1982       == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1983     {
1984       HANDLE reph;
1985       OBJECT_ATTRIBUTES attr;
1986       IO_STATUS_BLOCK io;
1987
1988       InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
1989                                   get_handle (), NULL);
1990       de->d_type = readdir_check_reparse_point (&attr);
1991       if (de->d_type == DT_DIR)
1992         {
1993           /* Volume mountpoints are treated as directories.  We have to fix
1994              the inode number, otherwise we have the inode number of the
1995              mount point, rather than the inode number of the toplevel
1996              directory of the mounted drive. */
1997           if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
1998                                       FILE_SHARE_VALID_FLAGS,
1999                                       FILE_OPEN_FOR_BACKUP_INTENT)))
2000             {
2001               de->d_ino = pc.get_ino_by_handle (reph);
2002               NtClose (reph);
2003             }
2004         }
2005     }
2006
2007   /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
2008      .lnk suffix and set d_type accordingly. */
2009   if ((attr & (FILE_ATTRIBUTE_DIRECTORY
2010                | FILE_ATTRIBUTE_REPARSE_POINT
2011                | FILE_ATTRIBUTE_READONLY)) == FILE_ATTRIBUTE_READONLY
2012       && fname->Length > 4 * sizeof (WCHAR))
2013     {
2014       UNICODE_STRING uname;
2015
2016       RtlInitCountedUnicodeString (&uname,
2017                                    fname->Buffer
2018                                    + fname->Length / sizeof (WCHAR) - 4,
2019                                    4 * sizeof (WCHAR));
2020       if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
2021         {
2022           tmp_pathbuf tp;
2023           char *file = tp.c_get ();
2024           char *p = stpcpy (file, pc.normalized_path);
2025           if (p[-1] != '/')
2026             *p++ = '/';
2027           sys_wcstombs (p, NT_MAX_PATH - (p - file),
2028                         fname->Buffer, fname->Length / sizeof (WCHAR));
2029           path_conv fpath (file, PC_SYM_NOFOLLOW);
2030           if (fpath.issymlink ())
2031             {
2032               fname->Length -= 4 * sizeof (WCHAR);
2033               de->d_type = DT_LNK;
2034             }
2035           else if (fpath.isfifo ())
2036             {
2037               fname->Length -= 4 * sizeof (WCHAR);
2038               de->d_type = DT_FIFO;
2039             }
2040           else if (fpath.is_fs_special ())
2041             {
2042               fname->Length -= 4 * sizeof (WCHAR);
2043               de->d_type = S_ISCHR (fpath.dev.mode) ? DT_CHR : DT_BLK;
2044             }
2045         }
2046     }
2047
2048   sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
2049                 fname->Length / sizeof (WCHAR));
2050
2051   /* Don't try to optimize relative to dir->__d_position.  On several
2052      filesystems it's no safe bet that "." and ".." entries always
2053      come first. */
2054   if (de->d_name[0] == '.')
2055     {
2056       if (de->d_name[1] == '\0')
2057         dir->__flags |= dirent_saw_dot;
2058       else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
2059         dir->__flags |= dirent_saw_dot_dot;
2060     }
2061   return 0;
2062 }
2063
2064 int
2065 fhandler_disk_file::readdir (DIR *dir, dirent *de)
2066 {
2067   int res = 0;
2068   NTSTATUS status = STATUS_SUCCESS;
2069   PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
2070   PWCHAR FileName;
2071   ULONG FileNameLength;
2072   ULONG FileAttributes = 0;
2073   IO_STATUS_BLOCK io;
2074   UNICODE_STRING fname;
2075
2076   /* d_cachepos always refers to the next cache entry to use.  If it's 0
2077      we must reload the cache. */
2078   if (d_cachepos (dir) == 0)
2079     {
2080       if ((dir->__flags & dirent_get_d_ino))
2081         {
2082           status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2083                                          d_cache (dir), DIR_BUF_SIZE,
2084                                          FileIdBothDirectoryInformation,
2085                                          FALSE, NULL, dir->__d_position == 0);
2086           /* FileIdBothDirectoryInformation isn't supported for remote drives
2087              on NT4 and 2K systems, and it's also not supported on 2K at all,
2088              when accessing network drives on any remote OS.  There are also
2089              hacked versions of Samba 3.0.x out there (Debian-based it seems),
2090              which return STATUS_NOT_SUPPORTED rather than handling this info
2091              class.  We just fall back to using a standard directory query in
2092              this case and note this case using the dirent_get_d_ino flag. */
2093           if (!NT_SUCCESS (status) && status != STATUS_NO_MORE_FILES
2094               && (status == STATUS_INVALID_LEVEL
2095                   || status == STATUS_NOT_SUPPORTED
2096                   || status == STATUS_INVALID_PARAMETER
2097                   || status == STATUS_INVALID_NETWORK_RESPONSE
2098                   || status == STATUS_INVALID_INFO_CLASS))
2099             dir->__flags &= ~dirent_get_d_ino;
2100           /* Something weird happens on Samba up to version 3.0.21c, which is
2101              fixed in 3.0.22.  FileIdBothDirectoryInformation seems to work
2102              nicely, but only up to the 128th entry in the directory.  After
2103              reaching this entry, the next call to NtQueryDirectoryFile
2104              (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
2105              Why should we care, we can just switch to
2106              FileBothDirectoryInformation, isn't it?  Nope!  The next call to
2107              NtQueryDirectoryFile(FileBothDirectoryInformation) actually
2108              returns STATUS_NO_MORE_FILES, regardless how many files are left
2109              unread in the directory.  This does not happen when using
2110              FileBothDirectoryInformation right from the start, but since
2111              we can't decide whether the server we're talking with has this
2112              bug or not, we end up serving Samba shares always in the slow
2113              mode using FileBothDirectoryInformation.  So, what we do here is
2114              to implement the solution suggested by Andrew Tridgell,  we just
2115              reread all entries up to dir->d_position using
2116              FileBothDirectoryInformation.
2117              However, We do *not* mark this server as broken and fall back to
2118              using FileBothDirectoryInformation further on.  This would slow
2119              down every access to such a server, even for directories under
2120              128 entries.  Also, bigger dirs only suffer from one additional
2121              call per full directory scan, which shouldn't be too big a hit.
2122              This can easily be changed if necessary. */
2123           if (status == STATUS_INVALID_LEVEL && dir->__d_position)
2124             {
2125               d_cachepos (dir) = 0;
2126               for (int cnt = 0; cnt < dir->__d_position; ++cnt)
2127                 {
2128                   if (d_cachepos (dir) == 0)
2129                     {
2130                       status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
2131                                            NULL, &io, d_cache (dir),
2132                                            DIR_BUF_SIZE,
2133                                            FileBothDirectoryInformation,
2134                                            FALSE, NULL, cnt == 0);
2135                       if (!NT_SUCCESS (status))
2136                         goto go_ahead;
2137                     }
2138                   buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
2139                                                          + d_cachepos (dir));
2140                   if (buf->NextEntryOffset == 0)
2141                     d_cachepos (dir) = 0;
2142                   else
2143                     d_cachepos (dir) += buf->NextEntryOffset;
2144                 }
2145               goto go_ahead;
2146             }
2147         }
2148       if (!(dir->__flags & dirent_get_d_ino))
2149         status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2150                                        d_cache (dir), DIR_BUF_SIZE,
2151                                        (dir->__flags & dirent_nfs_d_ino)
2152                                        ? FileNamesInformation
2153                                        : FileBothDirectoryInformation,
2154                                        FALSE, NULL, dir->__d_position == 0);
2155     }
2156
2157 go_ahead:
2158
2159   if (status == STATUS_NO_MORE_FILES)
2160     /*nothing*/;
2161   else if (!NT_SUCCESS (status))
2162     debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
2163                   status, RtlNtStatusToDosError (status));
2164   else
2165     {
2166       buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
2167       if (buf->NextEntryOffset == 0)
2168         d_cachepos (dir) = 0;
2169       else
2170         d_cachepos (dir) += buf->NextEntryOffset;
2171       if ((dir->__flags & dirent_get_d_ino))
2172         {
2173           FileName = buf->FileName;
2174           FileNameLength = buf->FileNameLength;
2175           FileAttributes = buf->FileAttributes;
2176           if ((dir->__flags & dirent_set_d_ino))
2177             de->d_ino = buf->FileId.QuadPart;
2178         }
2179       else if ((dir->__flags & dirent_nfs_d_ino))
2180         {
2181           FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
2182           FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
2183         }
2184       else
2185         {
2186           FileName = ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileName;
2187           FileNameLength =
2188                 ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileNameLength;
2189           FileAttributes =
2190                 ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileAttributes;
2191         }
2192       RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
2193       de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
2194       if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
2195         {
2196           /* Don't try to optimize relative to dir->__d_position.  On several
2197              filesystems it's no safe bet that "." and ".." entries always
2198              come first. */
2199           if (FileNameLength == sizeof (WCHAR) && FileName[0] == '.')
2200             de->d_ino = pc.get_ino_by_handle (get_handle ());
2201           else if (FileNameLength == 2 * sizeof (WCHAR)
2202                    && FileName[0] == L'.' && FileName[1] == L'.')
2203             {
2204               if (!(dir->__flags & dirent_isroot))
2205                 de->d_ino = readdir_get_ino (get_name (), true);
2206               else
2207                 de->d_ino = pc.get_ino_by_handle (get_handle ());
2208             }
2209           else
2210             {
2211               OBJECT_ATTRIBUTES attr;
2212               HANDLE hdl;
2213               NTSTATUS f_status;
2214
2215               InitializeObjectAttributes (&attr, &fname,
2216                                           pc.objcaseinsensitive (),
2217                                           get_handle (), NULL);
2218               /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2219                  NtOpenFile here returns the inode number of the symlink target,
2220                  rather than the inode number of the symlink itself.
2221
2222                  Worse, trying to open a symlink without setting the special
2223                  "ActOnSymlink" EA triggers a bug in Windows 7 which results
2224                  in a timeout of up to 20 seconds, followed by two exceptions
2225                  in the NT kernel.
2226
2227                  Since both results are far from desirable, we open symlinks
2228                  on NFS so that we get the right inode and a happy W7.
2229                  And, since some filesystems choke on the EAs, we don't
2230                  use them unconditionally. */
2231               f_status = (dir->__flags & dirent_nfs_d_ino)
2232                          ? NtCreateFile (&hdl, READ_CONTROL, &attr, &io,
2233                                          NULL, 0, FILE_SHARE_VALID_FLAGS,
2234                                          FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT,
2235                                          &nfs_aol_ffei, sizeof nfs_aol_ffei)
2236                          : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
2237                                        FILE_SHARE_VALID_FLAGS,
2238                                        FILE_OPEN_FOR_BACKUP_INTENT
2239                                        | FILE_OPEN_REPARSE_POINT);
2240               if (NT_SUCCESS (f_status))
2241                 {
2242                   /* We call NtQueryInformationFile here, rather than
2243                      pc.get_ino_by_handle(), otherwise we can't short-circuit
2244                      dirent_set_d_ino correctly. */
2245                   FILE_INTERNAL_INFORMATION fai;
2246                   f_status = NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
2247                                                      FileInternalInformation);
2248                   NtClose (hdl);
2249                   if (NT_SUCCESS (f_status))
2250                     {
2251                       if (pc.isgood_inode (fai.FileId.QuadPart))
2252                         de->d_ino = fai.FileId.QuadPart;
2253                       else
2254                         /* Untrusted file system.  Don't try to fetch inode
2255                            number again. */
2256                         dir->__flags &= ~dirent_set_d_ino;
2257                     }
2258                 }
2259             }
2260         }
2261     }
2262
2263   if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
2264                               FileAttributes, &fname)))
2265     dir->__d_position++;
2266   else if (!(dir->__flags & dirent_saw_dot))
2267     {
2268       strcpy (de->d_name , ".");
2269       de->d_ino = pc.get_ino_by_handle (get_handle ());
2270       de->d_type = DT_DIR;
2271       dir->__d_position++;
2272       dir->__flags |= dirent_saw_dot;
2273       res = 0;
2274     }
2275   else if (!(dir->__flags & dirent_saw_dot_dot))
2276     {
2277       strcpy (de->d_name , "..");
2278       if (!(dir->__flags & dirent_isroot))
2279         de->d_ino = readdir_get_ino (get_name (), true);
2280       else
2281         de->d_ino = pc.get_ino_by_handle (get_handle ());
2282       de->d_type = DT_DIR;
2283       dir->__d_position++;
2284       dir->__flags |= dirent_saw_dot_dot;
2285       res = 0;
2286     }
2287
2288   syscall_printf ("%d = readdir(%p, %p) (L\"%lS\" > \"%ls\") (attr %p > type %d)",
2289                   res, dir, &de, res ? NULL : &fname, res ? "***" : de->d_name,
2290                   FileAttributes, de->d_type);
2291   return res;
2292 }
2293
2294 long
2295 fhandler_disk_file::telldir (DIR *dir)
2296 {
2297   return dir->__d_position;
2298 }
2299
2300 void
2301 fhandler_disk_file::seekdir (DIR *dir, long loc)
2302 {
2303   rewinddir (dir);
2304   while (loc > dir->__d_position)
2305     if (!::readdir (dir))
2306       break;
2307 }
2308
2309 void
2310 fhandler_disk_file::rewinddir (DIR *dir)
2311 {
2312   d_cachepos (dir) = 0;
2313   if (wincap.has_buggy_restart_scan () && isremote ())
2314     {
2315       /* This works around a W2K bug.  The RestartScan parameter in calls
2316          to NtQueryDirectoryFile on remote shares is ignored, thus
2317          resulting in not being able to rewind on remote shares.  By
2318          reopening the directory, we get a fresh new directory pointer. */
2319       OBJECT_ATTRIBUTES attr;
2320       NTSTATUS status;
2321       IO_STATUS_BLOCK io;
2322       HANDLE new_dir;
2323
2324       pc.init_reopen_attr (&attr, get_handle ());
2325       status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2326                            &attr, &io, FILE_SHARE_VALID_FLAGS,
2327                            FILE_SYNCHRONOUS_IO_NONALERT
2328                            | FILE_OPEN_FOR_BACKUP_INTENT
2329                            | FILE_DIRECTORY_FILE);
2330       if (!NT_SUCCESS (stat))
2331         debug_printf ("Unable to reopen dir %s, NT error: %p",
2332                       get_name (), status);
2333       else
2334         {
2335           NtClose (get_handle ());
2336           set_io_handle (new_dir);
2337         }
2338     }
2339   dir->__d_position = 0;
2340   d_mounts (dir)->rewind ();
2341 }
2342
2343 int
2344 fhandler_disk_file::closedir (DIR *dir)
2345 {
2346   int res = 0;
2347   NTSTATUS status;
2348
2349   delete d_mounts (dir);
2350   if (!get_handle ())
2351     /* ignore */;
2352   else if (get_handle () == INVALID_HANDLE_VALUE)
2353     {
2354       set_errno (EBADF);
2355       res = -1;
2356     }
2357   else if (!NT_SUCCESS (status = NtClose (get_handle ())))
2358     {
2359       __seterrno_from_nt_status (status);
2360       res = -1;
2361     }
2362   syscall_printf ("%d = closedir(%p, %s)", res, dir, get_name ());
2363   return res;
2364 }
2365
2366 fhandler_cygdrive::fhandler_cygdrive () :
2367   fhandler_disk_file (), ndrives (0), pdrive (NULL)
2368 {
2369 }
2370
2371 int
2372 fhandler_cygdrive::open (int flags, mode_t mode)
2373 {
2374   if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2375     {
2376       set_errno (EEXIST);
2377       return 0;
2378     }
2379   if (flags & O_WRONLY)
2380     {
2381       set_errno (EISDIR);
2382       return 0;
2383     }
2384   flags |= O_DIROPEN;
2385   set_flags (flags);
2386   nohandle (true);
2387   return 1;
2388 }
2389
2390 int
2391 fhandler_cygdrive::close ()
2392 {
2393   return 0;
2394 }
2395
2396 void
2397 fhandler_cygdrive::set_drives ()
2398 {
2399   pdrive = pdrive_buf;
2400   ndrives = GetLogicalDriveStrings (sizeof pdrive_buf, pdrive_buf) / DRVSZ;
2401 }
2402
2403 int
2404 fhandler_cygdrive::fstat (struct __stat64 *buf)
2405 {
2406   fhandler_base::fstat (buf);
2407   buf->st_ino = 2;
2408   buf->st_mode = S_IFDIR | STD_RBITS | STD_XBITS;
2409   if (!ndrives)
2410     set_drives ();
2411   char flptst[] = "X:";
2412   int n = ndrives;
2413   for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
2414     if (is_floppy ((flptst[0] = *p, flptst))
2415         || GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
2416       n--;
2417   buf->st_nlink = n + 2;
2418   return 0;
2419 }
2420
2421 DIR *
2422 fhandler_cygdrive::opendir (int fd)
2423 {
2424   DIR *dir;
2425
2426   dir = fhandler_disk_file::opendir (fd);
2427   if (dir && !ndrives)
2428     set_drives ();
2429
2430   return dir;
2431 }
2432
2433 int
2434 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2435 {
2436   char flptst[] = "X:";
2437
2438   while (true)
2439     {
2440       if (!pdrive || !*pdrive)
2441         {
2442           if (!(dir->__flags & dirent_saw_dot))
2443             {
2444               de->d_name[0] = '.';
2445               de->d_name[1] = '\0';
2446               de->d_ino = 2;
2447             }
2448           return ENMFILE;
2449         }
2450       if (!is_floppy ((flptst[0] = *pdrive, flptst))
2451           && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2452         break;
2453       pdrive = strchr (pdrive, '\0') + 1;
2454     }
2455   *de->d_name = cyg_tolower (*pdrive);
2456   de->d_name[1] = '\0';
2457   user_shared->warned_msdos = true;
2458   de->d_ino = readdir_get_ino (pdrive, false);
2459   dir->__d_position++;
2460   pdrive = strchr (pdrive, '\0') + 1;
2461   syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2462   return 0;
2463 }
2464
2465 void
2466 fhandler_cygdrive::rewinddir (DIR *dir)
2467 {
2468   pdrive = pdrive_buf;
2469   dir->__d_position = 0;
2470 }
2471
2472 int
2473 fhandler_cygdrive::closedir (DIR *dir)
2474 {
2475   pdrive = pdrive_buf;
2476   return 0;
2477 }