OSDN Git Service

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