OSDN Git Service

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