OSDN Git Service

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