OSDN Git Service

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