OSDN Git Service

* fhandler_disk_file.cc (fhandler_disk_file::fchmod): Remove file
[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   /* Correct NTFS security attributes have higher priority */
850   if (!pc.has_acls ())
851     {
852       if (!NT_SUCCESS (status))
853         __seterrno_from_nt_status (status);
854       else
855         res = 0;
856     }
857
858 out:
859   if (oret)
860     close_fs ();
861
862   return res;
863 }
864
865 int __stdcall
866 fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
867 {
868   int oret = 0;
869
870   if (!pc.has_acls ())
871     {
872       /* fake - if not supported, pretend we're like win95
873          where it just works */
874       /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
875       return 0;
876     }
877
878   if (!get_handle ())
879     {
880       query_open (query_write_control);
881       if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
882         return -1;
883     }
884
885   mode_t attrib = 0;
886   if (pc.isdir ())
887     attrib |= S_IFDIR;
888   __uid32_t old_uid;
889   int res = get_file_attribute (get_handle (), pc, &attrib, &old_uid, NULL);
890   if (!res)
891     {
892       /* Typical Windows default ACLs can contain permissions for one
893          group, while being owned by another user/group.  The permission
894          bits returned above are pretty much useless then.  Creating a
895          new ACL with these useless permissions results in a potentially
896          broken symlink.  So what we do here is to set the underlying
897          permissions of symlinks to a sensible value which allows the
898          world to read the symlink and only the new owner to change it. */
899       if (pc.issymlink ())
900         attrib = S_IFLNK | STD_RBITS | STD_WBITS;
901       res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
902       /* If you're running a Samba server which has no winbidd running, the
903          uid<->SID mapping is disfunctional.  Even trying to chown to your
904          own account fails since the account used on the server is the UNIX
905          account which gets used for the standard user mapping.  This is a
906          default mechanism which doesn't know your real Windows SID.
907          There are two possible error codes in different Samba releases for
908          this situation, one of them is unfortunately the not very significant
909          STATUS_ACCESS_DENIED.  Instead of relying on the error codes, we're
910          using the below very simple heuristic.  If set_file_attribute failed,
911          and the original user account was either already unknown, or one of
912          the standard UNIX accounts, we're faking success. */
913       if (res == -1 && pc.fs_is_samba ())
914         {
915           cygsid sid;
916
917           if (old_uid == ILLEGAL_UID
918               || (sid.getfrompw (internal_getpwuid (old_uid))
919                   && EqualPrefixSid (sid, well_known_samba_unix_user_fake_sid)))
920             {
921               debug_printf ("Faking chown worked on standalone Samba");
922               res = 0;
923             }
924         }
925     }
926   if (oret)
927     close_fs ();
928
929   return res;
930 }
931
932 int _stdcall
933 fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp)
934 {
935   int res = -1;
936   int oret = 0;
937
938   if (!pc.has_acls ())
939     {
940 cant_access_acl:
941       switch (cmd)
942         {
943           struct __stat64 st;
944
945           case SETACL:
946             /* Open for writing required to be able to set ctime
947                (even though setting the ACL is just pretended). */
948             if (!get_handle ())
949               oret = open (O_WRONLY | O_BINARY, 0);
950             res = 0;
951             break;
952           case GETACL:
953             if (!aclbufp)
954               set_errno (EFAULT);
955             else if (nentries < MIN_ACL_ENTRIES)
956               set_errno (ENOSPC);
957             else
958               {
959                 if (!get_handle ())
960                   {
961                     query_open (query_read_attributes);
962                     oret = open (O_BINARY, 0);
963                   }
964                 if ((oret && !fstat_by_handle (&st))
965                     || !fstat_by_name (&st))
966                   {
967                     aclbufp[0].a_type = USER_OBJ;
968                     aclbufp[0].a_id = st.st_uid;
969                     aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
970                     aclbufp[1].a_type = GROUP_OBJ;
971                     aclbufp[1].a_id = st.st_gid;
972                     aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
973                     aclbufp[2].a_type = OTHER_OBJ;
974                     aclbufp[2].a_id = ILLEGAL_GID;
975                     aclbufp[2].a_perm = st.st_mode & S_IRWXO;
976                     aclbufp[3].a_type = CLASS_OBJ;
977                     aclbufp[3].a_id = ILLEGAL_GID;
978                     aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
979                     res = MIN_ACL_ENTRIES;
980                   }
981               }
982             break;
983           case GETACLCNT:
984             res = MIN_ACL_ENTRIES;
985             break;
986           default:
987             set_errno (EINVAL);
988             break;
989         }
990     }
991   else
992     {
993       if (!get_handle ())
994         {
995           query_open (cmd == SETACL ? query_write_control : query_read_control);
996           if (!(oret = open (O_BINARY, 0)))
997             {
998               if (cmd == GETACL || cmd == GETACLCNT)
999                 goto cant_access_acl;
1000               return -1;
1001             }
1002         }
1003       switch (cmd)
1004         {
1005           case SETACL:
1006             if (!aclsort32 (nentries, 0, aclbufp))
1007               {
1008                 bool rw = false;
1009                 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1010                 if (rw)
1011                   {
1012                     IO_STATUS_BLOCK io;
1013                     FILE_BASIC_INFORMATION fbi;
1014                     fbi.CreationTime.QuadPart
1015                     = fbi.LastAccessTime.QuadPart
1016                     = fbi.LastWriteTime.QuadPart
1017                     = fbi.ChangeTime.QuadPart = 0LL;
1018                     fbi.FileAttributes = (pc.file_attributes ()
1019                                           & ~FILE_ATTRIBUTE_READONLY)
1020                                          ?: FILE_ATTRIBUTE_NORMAL;
1021                     NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1022                                           FileBasicInformation);
1023                   }
1024               }
1025             break;
1026           case GETACL:
1027             if (!aclbufp)
1028               set_errno(EFAULT);
1029             else
1030               res = getacl (get_handle (), pc, nentries, aclbufp);
1031             break;
1032           case GETACLCNT:
1033             res = getacl (get_handle (), pc, 0, NULL);
1034             break;
1035           default:
1036             set_errno (EINVAL);
1037             break;
1038         }
1039     }
1040
1041   if (oret)
1042     close_fs ();
1043
1044   return res;
1045 }
1046
1047 ssize_t
1048 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1049 {
1050   if (pc.is_fs_special ())
1051     {
1052       set_errno (ENOTSUP);
1053       return -1;
1054     }
1055   return read_ea (get_handle (), pc, name, (char *) value, size);
1056 }
1057
1058 int
1059 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1060                                int flags)
1061 {
1062   if (pc.is_fs_special ())
1063     {
1064       set_errno (ENOTSUP);
1065       return -1;
1066     }
1067   return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1068 }
1069
1070 int
1071 fhandler_disk_file::fadvise (_off64_t offset, _off64_t length, int advice)
1072 {
1073   if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1074     {
1075       set_errno (EINVAL);
1076       return -1;
1077     }
1078
1079   /* Windows only supports advice flags for the whole file.  We're using
1080      a simplified test here so that we don't have to ask for the actual
1081      file size.  Length == 0 means all bytes starting at offset anyway.
1082      So we only actually follow the advice, if it's given for offset == 0. */
1083   if (offset != 0)
1084     return 0;
1085
1086   /* We only support normal and sequential mode for now.  Everything which
1087      is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1088   if (advice != POSIX_FADV_SEQUENTIAL)
1089     advice = POSIX_FADV_NORMAL;
1090
1091   IO_STATUS_BLOCK io;
1092   FILE_MODE_INFORMATION fmi;
1093   NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1094                                             &fmi, sizeof fmi,
1095                                             FileModeInformation);
1096   if (!NT_SUCCESS (status))
1097     __seterrno_from_nt_status (status);
1098   else
1099     {
1100       fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1101       if (advice == POSIX_FADV_SEQUENTIAL)
1102         fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1103       status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1104                                      FileModeInformation);
1105       if (NT_SUCCESS (status))
1106         return 0;
1107       __seterrno_from_nt_status (status);
1108     }
1109
1110   return -1;
1111 }
1112
1113 int
1114 fhandler_disk_file::ftruncate (_off64_t length, bool allow_truncate)
1115 {
1116   int res = -1;
1117
1118   if (length < 0 || !get_handle ())
1119     set_errno (EINVAL);
1120   else if (pc.isdir ())
1121     set_errno (EISDIR);
1122   else if (!(get_access () & GENERIC_WRITE))
1123     set_errno (EBADF);
1124   else
1125     {
1126       NTSTATUS status;
1127       IO_STATUS_BLOCK io;
1128       FILE_STANDARD_INFORMATION fsi;
1129       FILE_END_OF_FILE_INFORMATION feofi;
1130
1131       status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1132                                        FileStandardInformation);
1133       if (!NT_SUCCESS (status))
1134         {
1135           __seterrno_from_nt_status (status);
1136           return -1;
1137         }
1138
1139       /* If called through posix_fallocate, silently succeed if length
1140          is less than the file's actual length. */
1141       if (!allow_truncate && length < fsi.EndOfFile.QuadPart)
1142         return 0;
1143
1144       feofi.EndOfFile.QuadPart = length;
1145       /* Create sparse files only when called through ftruncate, not when
1146          called through posix_fallocate. */
1147       if (allow_truncate
1148           && (pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES)
1149           && length >= fsi.EndOfFile.QuadPart + (128 * 1024))
1150         {
1151           status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1152                                     FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1153           syscall_printf ("%p = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1154                           status, pc.get_nt_native_path ());
1155         }
1156       status = NtSetInformationFile (get_handle (), &io,
1157                                      &feofi, sizeof feofi,
1158                                      FileEndOfFileInformation);
1159       if (!NT_SUCCESS (status))
1160         __seterrno_from_nt_status (status);
1161       else
1162         res = 0;
1163     }
1164   return res;
1165 }
1166
1167 int
1168 fhandler_disk_file::link (const char *newpath)
1169 {
1170   path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
1171   if (newpc.error)
1172     {
1173       set_errno (newpc.error);
1174       return -1;
1175     }
1176
1177   if (newpc.exists ())
1178     {
1179       syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1180       set_errno (EEXIST);
1181       return -1;
1182     }
1183
1184   char new_buf[strlen (newpath) + 5];
1185   if (!newpc.error)
1186     {
1187       if (pc.is_lnk_special ())
1188         {
1189           /* Shortcut hack. */
1190           stpcpy (stpcpy (new_buf, newpath), ".lnk");
1191           newpath = new_buf;
1192           newpc.check (newpath, PC_SYM_NOFOLLOW);
1193         }
1194       else if (!pc.isdir ()
1195                && pc.is_binary ()
1196                && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1197                                              &ro_u_exe, TRUE)
1198                && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1199                                               &ro_u_exe, TRUE))
1200         {
1201           /* Executable hack. */
1202           stpcpy (stpcpy (new_buf, newpath), ".exe");
1203           newpath = new_buf;
1204           newpc.check (newpath, PC_SYM_NOFOLLOW);
1205         }
1206     }
1207
1208   HANDLE fh;
1209   NTSTATUS status;
1210   OBJECT_ATTRIBUTES attr;
1211   IO_STATUS_BLOCK io;
1212   status = NtOpenFile (&fh, READ_CONTROL,
1213                        pc.get_object_attr (attr, sec_none_nih), &io,
1214                        FILE_SHARE_VALID_FLAGS,
1215                        FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
1216   if (!NT_SUCCESS (status))
1217     {
1218       __seterrno_from_nt_status (status);
1219       return -1;
1220     }
1221   PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1222   ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1223   PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1224   pfli->ReplaceIfExists = FALSE;
1225   pfli->RootDirectory = NULL;
1226   memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1227   status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1228   NtClose (fh);
1229   if (!NT_SUCCESS (status))
1230     {
1231       if (status == STATUS_INVALID_DEVICE_REQUEST)
1232         {
1233           /* FS doesn't support hard links.  Try to copy file. */
1234           WCHAR pcw[(pc.get_nt_native_path ()->Length / sizeof (WCHAR)) + 1];
1235           WCHAR newpcw[(newpc.get_nt_native_path ()->Length / sizeof (WCHAR))
1236                        + 1];
1237           if (!CopyFileW (pc.get_wide_win32_path (pcw),
1238                           newpc.get_wide_win32_path (newpcw), TRUE))
1239             {
1240               __seterrno ();
1241               return -1;
1242             }
1243           SetFileAttributesW (newpcw, pc.file_attributes ());
1244         }
1245       else
1246         {
1247           __seterrno_from_nt_status (status);
1248           return -1;
1249         }
1250     }
1251   return 0;
1252 }
1253
1254 int
1255 fhandler_disk_file::utimens (const struct timespec *tvp)
1256 {
1257   return utimens_fs (tvp);
1258 }
1259
1260 int
1261 fhandler_base::utimens_fs (const struct timespec *tvp)
1262 {
1263   LARGE_INTEGER lastaccess, lastwrite;
1264   struct timespec timeofday;
1265   struct timespec tmp[2];
1266   bool closeit = false;
1267
1268   if (!get_handle ())
1269     {
1270       query_open (query_write_attributes);
1271       if (!open_fs (O_BINARY, 0))
1272         {
1273           /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1274              to change the timestamps.  Unfortunately it's not sufficient for a
1275              remote HPFS which requires GENERIC_WRITE, so we just retry to open
1276              for writing, though this fails for R/O files of course. */
1277           query_open (no_query);
1278           if (!open_fs (O_WRONLY | O_BINARY, 0))
1279             {
1280               syscall_printf ("Opening file failed");
1281               return -1;
1282             }
1283         }
1284       closeit = true;
1285     }
1286
1287   gettimeofday (reinterpret_cast<struct timeval *> (&timeofday), 0);
1288   timeofday.tv_nsec *= 1000;
1289   if (!tvp)
1290     tmp[1] = tmp[0] = timeofday;
1291   else
1292     {
1293       if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1294           || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1295         {
1296           set_errno (EINVAL);
1297           return -1;
1298         }
1299       tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1300       tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1301     }
1302   /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1303   timespec_to_filetime (&tmp[0], (FILETIME *) &lastaccess);
1304   timespec_to_filetime (&tmp[1], (FILETIME *) &lastwrite);
1305   debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
1306
1307   IO_STATUS_BLOCK io;
1308   FILE_BASIC_INFORMATION fbi;
1309   fbi.CreationTime.QuadPart = 0LL;
1310   fbi.LastAccessTime = lastaccess;
1311   fbi.LastWriteTime = lastwrite;
1312   fbi.ChangeTime.QuadPart = 0LL;
1313   fbi.FileAttributes = 0;
1314   NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1315                                           FileBasicInformation);
1316   if (closeit)
1317     close_fs ();
1318   /* Opening a directory on a 9x share from a NT machine works(!), but
1319      then NtSetInformationFile fails with STATUS_NOT_SUPPORTED.  Oh well... */
1320   if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1321     {
1322       __seterrno_from_nt_status (status);
1323       return -1;
1324     }
1325   return 0;
1326 }
1327
1328 fhandler_disk_file::fhandler_disk_file () :
1329   fhandler_base ()
1330 {
1331 }
1332
1333 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1334   fhandler_base ()
1335 {
1336   set_name (pc);
1337 }
1338
1339 int
1340 fhandler_disk_file::open (int flags, mode_t mode)
1341 {
1342   return open_fs (flags, mode);
1343 }
1344
1345 int
1346 fhandler_base::open_fs (int flags, mode_t mode)
1347 {
1348   /* Unfortunately NT allows to open directories for writing, but that's
1349      disallowed according to SUSv3. */
1350   if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1351     {
1352       set_errno (EISDIR);
1353       return 0;
1354     }
1355
1356   int res = fhandler_base::open (flags | O_DIROPEN, mode);
1357   if (!res)
1358     goto out;
1359
1360   /* This is for file systems known for having a buggy CreateFile call
1361      which might return a valid HANDLE without having actually opened
1362      the file.
1363      The only known file system to date is the SUN NFS Solstice Client 3.1
1364      which returns a valid handle when trying to open a file in a nonexistent
1365      directory. */
1366   if (pc.has_buggy_open () && !pc.exists ())
1367     {
1368       debug_printf ("Buggy open detected.");
1369       close_fs ();
1370       set_errno (ENOENT);
1371       return 0;
1372     }
1373
1374     ino = get_ino_by_handle (pc, get_handle ());
1375     /* A unique ID is necessary to recognize fhandler entries which are
1376        duplicated by dup(2) or fork(2). */
1377     AllocateLocallyUniqueId ((PLUID) &unique_id);
1378
1379 out:
1380   syscall_printf ("%d = fhandler_disk_file::open (%S, %p)", res,
1381                   pc.get_nt_native_path (), flags);
1382   return res;
1383 }
1384
1385 ssize_t __stdcall
1386 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
1387 {
1388   ssize_t res;
1389   _off64_t curpos = lseek (0, SEEK_CUR);
1390   if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1391     res = -1;
1392   else
1393     {
1394       size_t tmp_count = count;
1395       read (buf, tmp_count);
1396       if (lseek (curpos, SEEK_SET) >= 0)
1397         res = (ssize_t) tmp_count;
1398       else
1399         res = -1;
1400     }
1401   debug_printf ("%d = pread (%p, %d, %d)\n", res, buf, count, offset);
1402   return res;
1403 }
1404
1405 ssize_t __stdcall
1406 fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
1407 {
1408   int res;
1409   _off64_t curpos = lseek (0, SEEK_CUR);
1410   if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1411     res = curpos;
1412   else
1413     {
1414       res = (ssize_t) write (buf, count);
1415       if (lseek (curpos, SEEK_SET) < 0)
1416         res = -1;
1417     }
1418   debug_printf ("%d = pwrite (%p, %d, %d)\n", res, buf, count, offset);
1419   return res;
1420 }
1421
1422 int
1423 fhandler_disk_file::mkdir (mode_t mode)
1424 {
1425   int res = -1;
1426   SECURITY_ATTRIBUTES sa = sec_none_nih;
1427   security_descriptor sd;
1428
1429   /* See comments in fhander_base::open () for an explanation why we defer
1430      setting security attributes on remote files. */
1431   if (has_acls () && !pc.isremote ())
1432     set_security_attribute (pc, S_IFDIR | ((mode & 07777) & ~cygheap->umask),
1433                             &sa, sd);
1434
1435   NTSTATUS status;
1436   HANDLE dir;
1437   OBJECT_ATTRIBUTES attr;
1438   IO_STATUS_BLOCK io;
1439   PFILE_FULL_EA_INFORMATION p = NULL;
1440   ULONG plen = 0;
1441
1442   if (pc.fs_is_nfs ())
1443     {
1444       /* When creating a dir on an NFS share, we have to set the
1445          file mode by writing a NFS fattr3 structure with the
1446          correct mode bits set. */
1447       plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1448              + sizeof (fattr3);
1449       p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1450       p->NextEntryOffset = 0;
1451       p->Flags = 0;
1452       p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1453       p->EaValueLength = sizeof (fattr3);
1454       strcpy (p->EaName, NFS_V3_ATTR);
1455       fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1456       memset (nfs_attr, 0, sizeof (fattr3));
1457       nfs_attr->type = NF3DIR;
1458       nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1459     }
1460   status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
1461                          pc.get_object_attr (attr, sa), &io, NULL,
1462                          FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
1463                          FILE_CREATE,
1464                          FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1465                          | FILE_OPEN_FOR_BACKUP_INTENT,
1466                          p, plen);
1467   if (NT_SUCCESS (status))
1468     {
1469       if (has_acls () && pc.isremote ())
1470         set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
1471                             S_IFDIR | ((mode & 07777) & ~cygheap->umask));
1472       NtClose (dir);
1473       res = 0;
1474     }
1475   else
1476     __seterrno_from_nt_status (status);
1477
1478   return res;
1479 }
1480
1481 int
1482 fhandler_disk_file::rmdir ()
1483 {
1484   extern NTSTATUS unlink_nt (path_conv &pc);
1485
1486   if (!pc.isdir ())
1487     {
1488       set_errno (ENOTDIR);
1489       return -1;
1490     }
1491   if (!pc.exists ())
1492     {
1493       set_errno (ENOENT);
1494       return -1;
1495     }
1496
1497   NTSTATUS status = unlink_nt (pc);
1498
1499   /* Check for existence of remote dirs after trying to delete them.
1500      Two reasons:
1501      - Sometimes SMB indicates failure when it really succeeds.
1502      - Removeing a directory on a samba drive doesn't return an error if the
1503        directory can't be removed because it's not empty.  */
1504   if (isremote ())
1505     {
1506       OBJECT_ATTRIBUTES attr;
1507       FILE_BASIC_INFORMATION fbi;
1508
1509       if (NT_SUCCESS (NtQueryAttributesFile
1510                             (pc.get_object_attr (attr, sec_none_nih), &fbi)))
1511         status = STATUS_DIRECTORY_NOT_EMPTY;
1512       else
1513         status = STATUS_SUCCESS;
1514     }
1515   if (!NT_SUCCESS (status))
1516     {
1517       __seterrno_from_nt_status (status);
1518       return -1;
1519     }
1520   return 0;
1521 }
1522
1523 /* This is the minimal number of entries which fit into the readdir cache.
1524    The number of bytes allocated by the cache is determined by this number,
1525    To tune caching, just tweak this number.  To get a feeling for the size,
1526    the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes.  */
1527
1528 #define DIR_NUM_ENTRIES 100             /* Cache size 62404 bytes */
1529
1530 #define DIR_BUF_SIZE    (DIR_NUM_ENTRIES \
1531                          * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1532                             + (NAME_MAX + 1) * sizeof (WCHAR)))
1533
1534 struct __DIR_cache
1535 {
1536   char  __cache[DIR_BUF_SIZE];  /* W2K needs this buffer 8 byte aligned. */
1537   ULONG __pos;
1538 };
1539
1540 #define d_cachepos(d)   (((__DIR_cache *) (d)->__d_dirname)->__pos)
1541 #define d_cache(d)      (((__DIR_cache *) (d)->__d_dirname)->__cache)
1542
1543 #define d_mounts(d)     ((__DIR_mounts *) (d)->__d_internal)
1544
1545 DIR *
1546 fhandler_disk_file::opendir (int fd)
1547 {
1548   DIR *dir;
1549   DIR *res = NULL;
1550
1551   if (!pc.isdir ())
1552     set_errno (ENOTDIR);
1553   else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1554     set_errno (ENOMEM);
1555   else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1556            == NULL)
1557     {
1558       set_errno (ENOMEM);
1559       goto free_dir;
1560     }
1561   else if ((dir->__d_dirent =
1562             (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1563     {
1564       set_errno (ENOMEM);
1565       goto free_dirname;
1566     }
1567   else
1568     {
1569       cygheap_fdnew cfd;
1570       if (cfd < 0 && fd < 0)
1571         goto free_dirent;
1572
1573       dir->__d_dirent->__d_version = __DIRENT_VERSION;
1574       dir->__d_cookie = __DIRENT_COOKIE;
1575       dir->__handle = INVALID_HANDLE_VALUE;
1576       dir->__d_position = 0;
1577       dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1578                      ? dirent_isroot : 0;
1579       dir->__d_internal = (unsigned) new __DIR_mounts (get_name ());
1580       d_cachepos (dir) = 0;
1581
1582       if (!pc.iscygdrive ())
1583         {
1584           if (fd < 0)
1585             {
1586               /* opendir() case.  Initialize with given directory name and
1587                  NULL directory handle. */
1588               OBJECT_ATTRIBUTES attr;
1589               NTSTATUS status;
1590               IO_STATUS_BLOCK io;
1591
1592               status = NtOpenFile (&get_handle (),
1593                                    SYNCHRONIZE | FILE_LIST_DIRECTORY,
1594                                    pc.get_object_attr (attr, sec_none_nih),
1595                                    &io, FILE_SHARE_VALID_FLAGS,
1596                                    FILE_SYNCHRONOUS_IO_NONALERT
1597                                    | FILE_OPEN_FOR_BACKUP_INTENT
1598                                    | FILE_DIRECTORY_FILE);
1599               if (!NT_SUCCESS (status))
1600                 {
1601                   __seterrno_from_nt_status (status);
1602                   goto free_mounts;
1603                 }
1604             }
1605
1606           /* FileIdBothDirectoryInformation is apparently unsupported on
1607              XP when accessing directories on UDF.  When trying to use it
1608              so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1609              It's not clear if the call isn't also unsupported on other
1610              OS/FS combinations (say, Win2K/CDFS or so).  Instead of
1611              testing in readdir for yet another error code, let's use
1612              FileIdBothDirectoryInformation only on filesystems supporting
1613              persistent ACLs, FileDirectoryInformation otherwise.
1614
1615              NFS clients hide dangling symlinks from directory queries,
1616              unless you use the FileNamesInformation info class.
1617              On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
1618              works fine, but only if the NFS share is mounted to a drive
1619              letter.  TODO: We don't test that here for now, but it might
1620              be worth to test if there's a speed gain in using
1621              FileIdBothDirectoryInformation, because it doesn't require to
1622              open the file to read the inode number. */
1623           if (pc.hasgood_inode ())
1624             {
1625               dir->__flags |= dirent_set_d_ino;
1626               if (pc.fs_is_nfs ())
1627                 dir->__flags |= dirent_nfs_d_ino;
1628               else if (wincap.has_fileid_dirinfo ()
1629                        && !pc.has_buggy_fileid_dirinfo ())
1630                 dir->__flags |= dirent_get_d_ino;
1631             }
1632         }
1633       if (fd >= 0)
1634         dir->__d_fd = fd;
1635       else
1636         {
1637           /* Filling cfd with `this' (aka storing this in the file
1638              descriptor table should only happen after it's clear that
1639              opendir doesn't fail, otherwise we end up cfree'ing the
1640              fhandler twice, once in opendir() in dir.cc, the second
1641              time on exit.  Nasty, nasty... */
1642           cfd = this;
1643           dir->__d_fd = cfd;
1644           if (pc.iscygdrive ())
1645             cfd->nohandle (true);
1646         }
1647       set_close_on_exec (true);
1648       dir->__fh = this;
1649       res = dir;
1650     }
1651
1652   syscall_printf ("%p = opendir (%s)", res, get_name ());
1653   return res;
1654
1655 free_mounts:
1656   delete d_mounts (dir);
1657 free_dirent:
1658   free (dir->__d_dirent);
1659 free_dirname:
1660   free (dir->__d_dirname);
1661 free_dir:
1662   free (dir);
1663   return res;
1664 }
1665
1666 __ino64_t __stdcall
1667 readdir_get_ino (const char *path, bool dot_dot)
1668 {
1669   char *fname;
1670   struct __stat64 st;
1671   HANDLE hdl;
1672   OBJECT_ATTRIBUTES attr;
1673   IO_STATUS_BLOCK io;
1674   __ino64_t ino = 0;
1675
1676   if (dot_dot)
1677     {
1678       fname = (char *) alloca (strlen (path) + 4);
1679       char *c = stpcpy (fname, path);
1680       if (c[-1] != '/')
1681         *c++ = '/';
1682       strcpy (c, "..");
1683       path = fname;
1684     }
1685   path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN);
1686   if (pc.isspecial ())
1687     {
1688       if (!stat_worker (pc, &st))
1689         ino = st.st_ino;
1690     }
1691   else if (!pc.hasgood_inode ())
1692     ino = hash_path_name (0, pc.get_nt_native_path ());
1693   else if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1694                                    pc.get_object_attr (attr, sec_none_nih),
1695                                    &io, FILE_SHARE_VALID_FLAGS,
1696                                    FILE_OPEN_FOR_BACKUP_INTENT
1697                                    | (pc.is_rep_symlink ()
1698                                       ? FILE_OPEN_REPARSE_POINT : 0))))
1699     {
1700       ino = get_ino_by_handle (pc, hdl);
1701       if (!ino)
1702         ino = hash_path_name (0, pc.get_nt_native_path ());
1703       NtClose (hdl);
1704     }
1705   return ino;
1706 }
1707
1708 int
1709 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1710                                     DWORD attr, PUNICODE_STRING fname)
1711 {
1712   if (w32_err)
1713     {
1714       bool added = false;
1715       if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
1716         added = true;
1717       if (!added)
1718         return geterrno_from_win_error (w32_err);
1719
1720       attr = 0;
1721       dir->__flags &= ~dirent_set_d_ino;
1722     }
1723
1724   /* Set d_type if type can be determined from file attributes.
1725      FILE_ATTRIBUTE_SYSTEM ommitted to leave DT_UNKNOWN for old symlinks.
1726      For new symlinks, d_type will be reset to DT_UNKNOWN below.  */
1727   if (attr &&
1728       !(attr & (  ~FILE_ATTRIBUTE_VALID_FLAGS
1729                 | FILE_ATTRIBUTE_SYSTEM
1730                 | FILE_ATTRIBUTE_REPARSE_POINT)))
1731     {
1732       if (attr & FILE_ATTRIBUTE_DIRECTORY)
1733         de->d_type = DT_DIR;
1734       else
1735         de->d_type = DT_REG;
1736     }
1737
1738   /* Check for directory reparse point.  These are potential volume mount
1739      points which have another inode than the underlying directory. */
1740   if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1741       == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1742     {
1743       HANDLE reph;
1744       OBJECT_ATTRIBUTES attr;
1745       IO_STATUS_BLOCK io;
1746
1747       InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
1748                                   get_handle (), NULL);
1749       if (is_volume_mountpoint (&attr)
1750           && (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
1751                                       FILE_SHARE_VALID_FLAGS,
1752                                       FILE_OPEN_FOR_BACKUP_INTENT))))
1753         {
1754           de->d_ino = get_ino_by_handle (pc, reph);
1755           NtClose (reph);
1756         }
1757     }
1758
1759   /* Check for Windows shortcut. If it's a Cygwin or U/WIN
1760      symlink, drop the .lnk suffix. */
1761   if ((attr & FILE_ATTRIBUTE_READONLY) && fname->Length > 4 * sizeof (WCHAR))
1762     {
1763       UNICODE_STRING uname;
1764
1765       RtlInitCountedUnicodeString (&uname,
1766                                    fname->Buffer
1767                                    + fname->Length / sizeof (WCHAR) - 4,
1768                                    4 * sizeof (WCHAR));
1769       if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
1770         {
1771           tmp_pathbuf tp;
1772           UNICODE_STRING fbuf;
1773
1774           tp.u_get (&fbuf);
1775           RtlCopyUnicodeString (&fbuf, pc.get_nt_native_path ());
1776           RtlAppendUnicodeToString (&fbuf, L"\\");
1777           RtlAppendUnicodeStringToString (&fbuf, fname);
1778           fbuf.Buffer += 4; /* Skip leading \??\ */
1779           fbuf.Length -= 4 * sizeof (WCHAR);
1780           if (fbuf.Buffer[1] != L':') /* UNC path */
1781             {
1782               *(fbuf.Buffer += 2) = L'\\';
1783               fbuf.Length -= 2 * sizeof (WCHAR);
1784             }
1785           path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
1786           if (fpath.issymlink () || fpath.is_fs_special ())
1787             {
1788               fname->Length -= 4 * sizeof (WCHAR);
1789               de->d_type = DT_UNKNOWN;
1790             }
1791         }
1792     }
1793
1794   sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
1795                 fname->Length / sizeof (WCHAR));
1796
1797   if (dir->__d_position == 0 && !strcmp (de->d_name, "."))
1798     dir->__flags |= dirent_saw_dot;
1799   else if (dir->__d_position == 1 && !strcmp (de->d_name, ".."))
1800     dir->__flags |= dirent_saw_dot_dot;
1801   return 0;
1802 }
1803
1804 int
1805 fhandler_disk_file::readdir (DIR *dir, dirent *de)
1806 {
1807   int res = 0;
1808   NTSTATUS status = STATUS_SUCCESS;
1809   PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
1810   PWCHAR FileName;
1811   ULONG FileNameLength;
1812   ULONG FileAttributes = 0;
1813   IO_STATUS_BLOCK io;
1814   UNICODE_STRING fname;
1815
1816   /* d_cachepos always refers to the next cache entry to use.  If it's 0
1817      we must reload the cache. */
1818   if (d_cachepos (dir) == 0)
1819     {
1820       if ((dir->__flags & dirent_get_d_ino))
1821         {
1822           status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1823                                          d_cache (dir), DIR_BUF_SIZE,
1824                                          FileIdBothDirectoryInformation,
1825                                          FALSE, NULL, dir->__d_position == 0);
1826           /* FileIdBothDirectoryInformation isn't supported for remote drives
1827              on NT4 and 2K systems, and it's also not supported on 2K at all,
1828              when accessing network drives on any remote OS.  We just fall
1829              back to using a standard directory query in this case and note
1830              this case using the dirent_get_d_ino flag. */
1831           if (status == STATUS_INVALID_LEVEL
1832               || status == STATUS_INVALID_PARAMETER
1833               || status == STATUS_INVALID_INFO_CLASS)
1834             dir->__flags &= ~dirent_get_d_ino;
1835           /* Something weird happens on Samba up to version 3.0.21c, which is
1836              fixed in 3.0.22.  FileIdBothDirectoryInformation seems to work
1837              nicely, but only up to the 128th entry in the directory.  After
1838              reaching this entry, the next call to NtQueryDirectoryFile
1839              (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
1840              Why should we care, we can just switch to FileDirectoryInformation,
1841              isn't it?  Nope!  The next call to
1842                NtQueryDirectoryFile(FileDirectoryInformation)
1843              actually returns STATUS_NO_MORE_FILES, regardless how many files
1844              are left unread in the directory.  This does not happen when using
1845              FileDirectoryInformation right from the start, but since
1846              we can't decide whether the server we're talking with has this
1847              bug or not, we end up serving Samba shares always in the slow
1848              mode using FileDirectoryInformation.  So, what we do here is
1849              to implement the solution suggested by Andrew Tridgell,  we just
1850              reread all entries up to dir->d_position using
1851              FileDirectoryInformation.
1852              However, We do *not* mark this server as broken and fall back to
1853              using FileDirectoryInformation further on.  This would slow
1854              down every access to such a server, even for directories under
1855              128 entries.  Also, bigger dirs only suffer from one additional
1856              call per full directory scan, which shouldn't be too big a hit.
1857              This can easily be changed if necessary. */
1858           if (status == STATUS_INVALID_LEVEL && dir->__d_position)
1859             {
1860               d_cachepos (dir) = 0;
1861               for (int cnt = 0; cnt < dir->__d_position; ++cnt)
1862                 {
1863                   if (d_cachepos (dir) == 0)
1864                     {
1865                       status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
1866                                            NULL, &io, d_cache (dir),
1867                                            DIR_BUF_SIZE,
1868                                            FileDirectoryInformation,
1869                                            FALSE, NULL, cnt == 0);
1870                       if (!NT_SUCCESS (status))
1871                         goto go_ahead;
1872                     }
1873                   buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
1874                                                          + d_cachepos (dir));
1875                   if (buf->NextEntryOffset == 0)
1876                     d_cachepos (dir) = 0;
1877                   else
1878                     d_cachepos (dir) += buf->NextEntryOffset;
1879                 }
1880               goto go_ahead;
1881             }
1882         }
1883       if (!(dir->__flags & dirent_get_d_ino))
1884         status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1885                                        d_cache (dir), DIR_BUF_SIZE,
1886                                        (dir->__flags & dirent_nfs_d_ino)
1887                                        ? FileNamesInformation
1888                                        : FileDirectoryInformation,
1889                                        FALSE, NULL, dir->__d_position == 0);
1890     }
1891
1892 go_ahead:
1893
1894   if (status == STATUS_NO_MORE_FILES)
1895     /*nothing*/;
1896   else if (!NT_SUCCESS (status))
1897     debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
1898                   status, RtlNtStatusToDosError (status));
1899   else
1900     {
1901       buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
1902       if (buf->NextEntryOffset == 0)
1903         d_cachepos (dir) = 0;
1904       else
1905         d_cachepos (dir) += buf->NextEntryOffset;
1906       if ((dir->__flags & dirent_get_d_ino))
1907         {
1908           FileName = buf->FileName;
1909           FileNameLength = buf->FileNameLength;
1910           FileAttributes = buf->FileAttributes;
1911           if ((dir->__flags & dirent_set_d_ino))
1912             de->d_ino = buf->FileId.QuadPart;
1913         }
1914       else if ((dir->__flags & dirent_nfs_d_ino))
1915         {
1916           FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
1917           FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
1918         }
1919       else
1920         {
1921           FileName = ((PFILE_DIRECTORY_INFORMATION) buf)->FileName;
1922           FileNameLength = ((PFILE_DIRECTORY_INFORMATION) buf)->FileNameLength;
1923           FileAttributes = ((PFILE_DIRECTORY_INFORMATION) buf)->FileAttributes;
1924         }
1925       RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
1926       de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
1927       if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
1928         {
1929           OBJECT_ATTRIBUTES attr;
1930
1931           if (dir->__d_position == 0 && FileNameLength == 2
1932               && FileName[0] == '.')
1933             de->d_ino = get_ino_by_handle (pc, get_handle ());
1934           else if (dir->__d_position == 1 && FileNameLength == 4
1935                    && FileName[0] == L'.' && FileName[1] == L'.')
1936             if (!(dir->__flags & dirent_isroot))
1937               de->d_ino = readdir_get_ino (get_name (), true);
1938             else
1939               de->d_ino = get_ino_by_handle (pc, get_handle ());
1940           else
1941             {
1942               HANDLE hdl;
1943
1944               InitializeObjectAttributes (&attr, &fname,
1945                                           pc.objcaseinsensitive (),
1946                                           get_handle (), NULL);
1947               if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
1948                                           FILE_SHARE_VALID_FLAGS,
1949                                           FILE_OPEN_FOR_BACKUP_INTENT
1950                                           | FILE_OPEN_REPARSE_POINT)))
1951                 {
1952                   de->d_ino = get_ino_by_handle (pc, hdl);
1953                   NtClose (hdl);
1954                 }
1955             }
1956           /* Untrusted file system.  Don't try to fetch inode number again. */
1957           if (de->d_ino == 0)
1958             dir->__flags &= ~dirent_set_d_ino;
1959         }
1960     }
1961
1962   if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
1963                               buf ? FileAttributes : 0, &fname)))
1964     dir->__d_position++;
1965   else if (!(dir->__flags & dirent_saw_dot))
1966     {
1967       strcpy (de->d_name , ".");
1968       de->d_ino = get_ino_by_handle (pc, get_handle ());
1969       dir->__d_position++;
1970       dir->__flags |= dirent_saw_dot;
1971       res = 0;
1972     }
1973   else if (!(dir->__flags & dirent_saw_dot_dot))
1974     {
1975       strcpy (de->d_name , "..");
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       dir->__d_position++;
1981       dir->__flags |= dirent_saw_dot_dot;
1982       res = 0;
1983     }
1984
1985   syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, &de, res ? "***" : de->d_name);
1986   return res;
1987 }
1988
1989 _off64_t
1990 fhandler_disk_file::telldir (DIR *dir)
1991 {
1992   return dir->__d_position;
1993 }
1994
1995 void
1996 fhandler_disk_file::seekdir (DIR *dir, _off64_t loc)
1997 {
1998   rewinddir (dir);
1999   while (loc > dir->__d_position)
2000     if (!::readdir (dir))
2001       break;
2002 }
2003
2004 void
2005 fhandler_disk_file::rewinddir (DIR *dir)
2006 {
2007   d_cachepos (dir) = 0;
2008   if (wincap.has_buggy_restart_scan () && isremote ())
2009     {
2010       /* This works around a W2K bug.  The RestartScan parameter in calls
2011          to NtQueryDirectoryFile on remote shares is ignored, thus
2012          resulting in not being able to rewind on remote shares.  By
2013          reopening the directory, we get a fresh new directory pointer. */
2014       OBJECT_ATTRIBUTES attr;
2015       NTSTATUS status;
2016       IO_STATUS_BLOCK io;
2017       HANDLE new_dir;
2018
2019       InitializeObjectAttributes (&attr, &ro_u_empty, pc.objcaseinsensitive (),
2020                                   get_handle (), NULL);
2021       status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2022                            &attr, &io, FILE_SHARE_VALID_FLAGS,
2023                            FILE_SYNCHRONOUS_IO_NONALERT
2024                            | FILE_OPEN_FOR_BACKUP_INTENT
2025                            | FILE_DIRECTORY_FILE);
2026       if (!NT_SUCCESS (stat))
2027         debug_printf ("Unable to reopen dir %s, NT error: %p",
2028                       get_name (), status);
2029       else
2030         {
2031           NtClose (get_handle ());
2032           set_io_handle (new_dir);
2033         }
2034     }
2035   dir->__d_position = 0;
2036   d_mounts (dir)->rewind ();
2037 }
2038
2039 int
2040 fhandler_disk_file::closedir (DIR *dir)
2041 {
2042   int res = 0;
2043   NTSTATUS status;
2044
2045   delete d_mounts (dir);
2046   if (!get_handle ())
2047     /* ignore */;
2048   else if (get_handle () == INVALID_HANDLE_VALUE)
2049     {
2050       set_errno (EBADF);
2051       res = -1;
2052     }
2053   else if (!NT_SUCCESS (status = NtClose (get_handle ())))
2054     {
2055       __seterrno_from_nt_status (status);
2056       res = -1;
2057     }
2058   syscall_printf ("%d = closedir (%p, %s)", res, dir, get_name ());
2059   return res;
2060 }
2061
2062 fhandler_cygdrive::fhandler_cygdrive () :
2063   fhandler_disk_file (), ndrives (0), pdrive (NULL)
2064 {
2065 }
2066
2067 int
2068 fhandler_cygdrive::open (int flags, mode_t mode)
2069 {
2070   if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2071     {
2072       set_errno (EEXIST);
2073       return 0;
2074     }
2075   if (flags & O_WRONLY)
2076     {
2077       set_errno (EISDIR);
2078       return 0;
2079     }
2080   flags |= O_DIROPEN;
2081   set_flags (flags);
2082   nohandle (true);
2083   return 1;
2084 }
2085
2086 int
2087 fhandler_cygdrive::close ()
2088 {
2089   return 0;
2090 }
2091
2092 #define DRVSZ sizeof ("x:\\")
2093 void
2094 fhandler_cygdrive::set_drives ()
2095 {
2096   const int len = 2 + 26 * DRVSZ;
2097   char *p = const_cast<char *> (get_win32_name ());
2098   pdrive = p;
2099   ndrives = GetLogicalDriveStrings (len, p) / DRVSZ;
2100 }
2101
2102 int
2103 fhandler_cygdrive::fstat (struct __stat64 *buf)
2104 {
2105   fhandler_base::fstat (buf);
2106   buf->st_ino = 2;
2107   buf->st_mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
2108   if (!ndrives)
2109     set_drives ();
2110   char flptst[] = "X:";
2111   int n = ndrives;
2112   for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
2113     if (is_floppy ((flptst[0] = *p, flptst))
2114         || GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
2115       --n;
2116   buf->st_nlink = n + 2;
2117   return 0;
2118 }
2119
2120 DIR *
2121 fhandler_cygdrive::opendir (int fd)
2122 {
2123   DIR *dir;
2124
2125   dir = fhandler_disk_file::opendir (fd);
2126   if (dir && !ndrives)
2127     set_drives ();
2128
2129   return dir;
2130 }
2131
2132 int
2133 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2134 {
2135   char flptst[] = "X:";
2136
2137   while (true)
2138     {
2139       if (!pdrive || !*pdrive)
2140         {
2141           if (!(dir->__flags & dirent_saw_dot))
2142             {
2143               de->d_name[0] = '.';
2144               de->d_name[1] = '\0';
2145               de->d_ino = 2;
2146             }
2147           return ENMFILE;
2148         }
2149       if (!is_floppy ((flptst[0] = *pdrive, flptst))
2150           && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2151         break;
2152       pdrive = strchr (pdrive, '\0') + 1;
2153     }
2154   *de->d_name = cyg_tolower (*pdrive);
2155   de->d_name[1] = '\0';
2156   user_shared->warned_msdos = true;
2157   de->d_ino = readdir_get_ino (pdrive, false);
2158   dir->__d_position++;
2159   pdrive = strchr (pdrive, '\0') + 1;
2160   syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2161   return 0;
2162 }
2163
2164 void
2165 fhandler_cygdrive::rewinddir (DIR *dir)
2166 {
2167   pdrive = get_win32_name ();
2168   dir->__d_position = 0;
2169 }
2170
2171 int
2172 fhandler_cygdrive::closedir (DIR *dir)
2173 {
2174   pdrive = get_win32_name ();
2175   return 0;
2176 }