OSDN Git Service

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