d54d3747eae6defdc67b8b9775fe91fad3353b07
[newlib-cygwin.git] / winsup / cygwin / fhandler / disk_file.cc
1 /* fhandler_disk_file.cc
2
3 This file is part of Cygwin.
4
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
7 details. */
8
9 #include "winsup.h"
10 #include <winioctl.h>
11 #include <lm.h>
12 #include <stdlib.h>
13 #include <cygwin/acl.h>
14 #include <sys/statvfs.h>
15 #include "cygerrno.h"
16 #include "security.h"
17 #include "path.h"
18 #include "fhandler.h"
19 #include "dtable.h"
20 #include "cygheap.h"
21 #include "shared_info.h"
22 #include "pinfo.h"
23 #include "ntdll.h"
24 #include "tls_pbuf.h"
25 #include "devices.h"
26 #include "ldap.h"
27 #include <aio.h>
28 #include <fcntl.h>
29 #include <cygwin/fs.h>
30
31 #define _LIBC
32 #include <dirent.h>
33
34 enum __DIR_mount_type {
35   __DIR_mount_none = 0,
36   __DIR_mount_target,
37   __DIR_mount_virt_target
38 };
39
40 class __DIR_mounts
41 {
42   int            count;
43   const char    *parent_dir;
44   size_t         parent_dir_len;
45   UNICODE_STRING mounts[MAX_MOUNTS];
46   bool           found[MAX_MOUNTS + 3];
47   UNICODE_STRING cygdrive;
48
49 #define __DIR_PROC      (MAX_MOUNTS)
50 #define __DIR_CYGDRIVE  (MAX_MOUNTS+1)
51 #define __DIR_DEV       (MAX_MOUNTS+2)
52
53 public:
54   __DIR_mounts (const char *posix_path)
55   : parent_dir (posix_path)
56     {
57       parent_dir_len = strlen (parent_dir);
58       count = mount_table->get_mounts_here (parent_dir, parent_dir_len, mounts,
59                                             &cygdrive);
60       rewind ();
61     }
62   ~__DIR_mounts ()
63     {
64       mount_table->free_mounts_here (mounts, count, &cygdrive);
65     }
66   /* For an entry within this dir, check if a mount point exists. */
67   bool check_mount (PUNICODE_STRING fname)
68     {
69       if (parent_dir_len == 1)  /* root dir */
70         {
71           if (RtlEqualUnicodeString (fname, &ro_u_proc, FALSE))
72             {
73               found[__DIR_PROC] = true;
74               return true;
75             }
76           if (RtlEqualUnicodeString (fname, &ro_u_dev, FALSE))
77             {
78               found[__DIR_DEV] = true;
79               return true;
80             }
81           if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2
82               && RtlEqualUnicodeString (fname, &cygdrive, FALSE))
83             {
84               found[__DIR_CYGDRIVE] = true;
85               return true;
86             }
87         }
88       for (int i = 0; i < count; ++i)
89         if (RtlEqualUnicodeString (fname, &mounts[i], FALSE))
90           {
91             found[i] = true;
92             return true;
93           }
94       return false;
95     }
96   /* On each call, add another mount point within this directory, which is
97      not backed by a real subdir. */
98   __DIR_mount_type check_missing_mount (PUNICODE_STRING retname = NULL)
99     {
100       for (int i = 0; i < count; ++i)
101         if (!found[i])
102           {
103             found[i] = true;
104             if (retname)
105               *retname = mounts[i];
106             return __DIR_mount_target;
107           }
108       if (parent_dir_len == 1)  /* root dir */
109         {
110           if (!found[__DIR_PROC])
111             {
112               found[__DIR_PROC] = true;
113               if (retname)
114                 *retname = ro_u_proc;
115               return __DIR_mount_virt_target;
116             }
117           if (!found[__DIR_DEV])
118             {
119               found[__DIR_DEV] = true;
120               if (retname)
121                 *retname = ro_u_dev;
122               return __DIR_mount_virt_target;
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 __DIR_mount_virt_target;
132                 }
133             }
134         }
135       return __DIR_mount_none;
136     }
137     void rewind () { memset (found, 0, sizeof found); }
138 };
139
140 inline bool
141 path_conv::isgood_inode (ino_t ino) const
142 {
143   /* If the FS doesn't support nonambiguous inode numbers anyway, bail out
144      immediately. */
145   if (!hasgood_inode ())
146     return false;
147   /* If the inode numbers are 64 bit numbers or if it's a local FS, they
148      are to be trusted. */
149   if (ino > UINT32_MAX || !isremote ())
150     return true;
151   /* Starting with version 3.5.4, Samba returns the real inode numbers, if
152      the file is on the same device as the root of the share (Samba function
153      get_FileIndex). */
154   if (fs_is_samba () && fs.samba_version () < 0x03050400)
155     return false;
156   /* Otherwise, trust the inode numbers unless proved otherwise. */
157   return true;
158 }
159
160 /* Check reparse point to determine if it should be treated as a
161    posix symlink or as a normal file/directory.  Logic is explained
162    in detail in check_reparse_point_target in path.cc. */
163 static inline bool
164 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
165 {
166   NTSTATUS status;
167   HANDLE reph;
168   IO_STATUS_BLOCK io;
169   tmp_pathbuf tp;
170   UNICODE_STRING symbuf;
171   bool ret = false;
172
173   status = NtOpenFile (&reph, READ_CONTROL, attr, &io, FILE_SHARE_VALID_FLAGS,
174                        FILE_OPEN_NO_RECALL
175                        | FILE_OPEN_FOR_BACKUP_INTENT
176                        | FILE_OPEN_REPARSE_POINT);
177   if (NT_SUCCESS (status))
178     {
179       PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
180       ret = (check_reparse_point_target (reph, remote, rp, &symbuf) > 0);
181       NtClose (reph);
182     }
183   return ret;
184 }
185
186 inline ino_t
187 path_conv::get_ino_by_handle (HANDLE hdl)
188 {
189   IO_STATUS_BLOCK io;
190   FILE_INTERNAL_INFORMATION fii;
191
192   if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fii, sizeof fii,
193                                           FileInternalInformation))
194       && isgood_inode (fii.IndexNumber.QuadPart))
195     return fii.IndexNumber.QuadPart;
196   return 0;
197 }
198
199 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
200    This returns the content of a struct fattr3 as defined in RFC 1813.
201    The content is the NFS equivalent of struct stat. so there's not much
202    to do here except for copying. */
203 int
204 fhandler_base::fstat_by_nfs_ea (struct stat *buf)
205 {
206   fattr3 *nfs_attr = pc.nfsattr ();
207   PWCHAR domain;
208   cyg_ldap cldap;
209   bool ldap_open = false;
210
211   /* NFS stumbles over its own caching.  If you write to the file,
212      a subsequent fstat does not return the actual size of the file,
213      but the size at the time the handle has been opened.  Unless
214      access through another handle invalidates the caching within the
215      NFS client.  Skip this for Cygwin-created Symlinks playing FIFOs
216      (this sets the filler1 member to NF3FIFO). */
217   if (get_handle () && nfs_attr->filler1 != NF3FIFO)
218     {
219       if (get_access () & GENERIC_WRITE)
220         FlushFileBuffers (get_handle ());
221       pc.get_finfo (get_handle ());
222     }
223   buf->st_dev = nfs_attr->fsid;
224   buf->st_ino = nfs_attr->fileid;
225   buf->st_mode = (nfs_attr->mode & 0xfff)
226                  | nfs_type_mapping[nfs_attr->type & 7];
227   buf->st_nlink = nfs_attr->nlink;
228   if (cygheap->pg.nss_pwd_db ())
229     {
230       /* Try to map UNIX uid/gid to Cygwin uid/gid.  If there's no mapping in
231          the cache, try to fetch it from the configured RFC 2307 domain (see
232          last comment in cygheap_domain_info::init() for more information) and
233          add it to the mapping cache. */
234       buf->st_uid = cygheap->ugid_cache.get_uid (nfs_attr->uid);
235       if (buf->st_uid == ILLEGAL_UID)
236         {
237           uid_t map_uid = ILLEGAL_UID;
238
239           domain = cygheap->dom.get_rfc2307_domain ();
240           if ((ldap_open = (cldap.open (domain) == NO_ERROR)))
241             map_uid = cldap.remap_uid (nfs_attr->uid);
242           if (map_uid == ILLEGAL_UID)
243             map_uid = MAP_UNIX_TO_CYGWIN_ID (nfs_attr->uid);
244           cygheap->ugid_cache.add_uid (nfs_attr->uid, map_uid);
245           buf->st_uid = map_uid;
246         }
247     }
248   else /* fake files being owned by current user. */
249     buf->st_uid = myself->uid;
250   if (cygheap->pg.nss_grp_db ())
251     {
252       /* See above */
253       buf->st_gid = cygheap->ugid_cache.get_gid (nfs_attr->gid);
254       if (buf->st_gid == ILLEGAL_GID)
255         {
256           gid_t map_gid = ILLEGAL_GID;
257
258           domain = cygheap->dom.get_rfc2307_domain ();
259           if ((ldap_open || cldap.open (domain) == NO_ERROR))
260             map_gid = cldap.remap_gid (nfs_attr->gid);
261           if (map_gid == ILLEGAL_GID)
262             map_gid = MAP_UNIX_TO_CYGWIN_ID (nfs_attr->gid);
263           cygheap->ugid_cache.add_gid (nfs_attr->gid, map_gid);
264           buf->st_gid = map_gid;
265         }
266     }
267   else /* fake files being owned by current group. */
268     buf->st_gid = myself->gid;
269   buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
270                           nfs_attr->rdev.specdata2);
271   buf->st_size = nfs_attr->size;
272   buf->st_blksize = PREFERRED_IO_BLKSIZE;
273   buf->st_blocks = (nfs_attr->used + S_BLKSIZE - 1) / S_BLKSIZE;
274   buf->st_atim.tv_sec = nfs_attr->atime.tv_sec;
275   buf->st_atim.tv_nsec = nfs_attr->atime.tv_nsec;
276   buf->st_mtim.tv_sec = nfs_attr->mtime.tv_sec;
277   buf->st_mtim.tv_nsec = nfs_attr->mtime.tv_nsec;
278   buf->st_ctim.tv_sec = nfs_attr->ctime.tv_sec;
279   buf->st_ctim.tv_nsec = nfs_attr->ctime.tv_nsec;
280   return 0;
281 }
282
283 int
284 fhandler_base::fstat_by_handle (struct stat *buf)
285 {
286   HANDLE h = get_stat_handle ();
287   NTSTATUS status = 0;
288
289   /* If the file has been opened for other purposes than stat, we can't rely
290      on the information stored in pc.fai.  So we overwrite them here. */
291   if (get_handle ())
292     {
293       status = pc.get_finfo (h);
294       if (!NT_SUCCESS (status))
295        {
296          debug_printf ("%y = NtQueryInformationFile(%S, FileAllInformation)",
297                        status, pc.get_nt_native_path ());
298          return -1;
299        }
300     }
301   if (pc.isgood_inode (pc.fai ()->InternalInformation.IndexNumber.QuadPart))
302     ino = pc.fai ()->InternalInformation.IndexNumber.QuadPart;
303   return fstat_helper (buf);
304 }
305
306 int
307 fhandler_base::fstat_by_name (struct stat *buf)
308 {
309   NTSTATUS status;
310   OBJECT_ATTRIBUTES attr;
311   IO_STATUS_BLOCK io;
312   UNICODE_STRING dirname;
313   UNICODE_STRING basename;
314   HANDLE dir;
315   struct {
316     FILE_ID_BOTH_DIR_INFORMATION fdi;
317     WCHAR buf[NAME_MAX + 1];
318   } fdi_buf;
319
320   if (!ino && pc.hasgood_inode () && !pc.has_buggy_fileid_dirinfo ())
321     {
322       RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
323       InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
324                                   NULL, NULL);
325       status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
326                            &attr, &io, FILE_SHARE_VALID_FLAGS,
327                            FILE_SYNCHRONOUS_IO_NONALERT
328                            | FILE_OPEN_FOR_BACKUP_INTENT
329                            | FILE_DIRECTORY_FILE);
330       if (!NT_SUCCESS (status))
331         debug_printf ("%y = NtOpenFile(%S)", status, &dirname);
332       else
333         {
334           status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
335                                          &fdi_buf.fdi, sizeof fdi_buf,
336                                          FileIdBothDirectoryInformation,
337                                          TRUE, &basename, TRUE);
338           NtClose (dir);
339           if (!NT_SUCCESS (status))
340             debug_printf ("%y = NtQueryDirectoryFile(%S)", status, &dirname);
341           else
342             ino = fdi_buf.fdi.FileId.QuadPart;
343         }
344     }
345   return fstat_helper (buf);
346 }
347
348 int
349 fhandler_base::fstat_fs (struct stat *buf)
350 {
351   int res = -1;
352   int oret;
353   int open_flags = O_RDONLY | O_BINARY;
354
355   if (get_stat_handle ())
356     {
357       if (!nohandle ())
358         {
359           if (pc.fs_is_nfs ())
360             res = fstat_by_nfs_ea (buf);
361           else if (!is_fs_special () || get_flags () & O_PATH)
362             res = fstat_by_handle (buf);
363         }
364       if (res)
365         res = fstat_by_name (buf);
366       return res;
367     }
368   /* First try to open with generic read access.  This allows to read the file
369      in fstat_helper (when checking for executability) without having to
370      re-open it.  Opening a file can take a lot of time on network drives
371      so we try to avoid that. */
372   oret = open_fs (open_flags, 0);
373   if (!oret)
374     {
375       query_open (query_read_attributes);
376       oret = open_fs (open_flags, 0);
377     }
378   if (oret)
379     {
380       /* We now have a valid handle, regardless of the "nohandle" state.
381          Since fhandler_base::close only calls CloseHandle if !nohandle,
382          we have to set it to false before calling close and restore
383          the state afterwards. */
384       res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
385       bool no_handle = nohandle ();
386       nohandle (false);
387       close_fs ();
388       nohandle (no_handle);
389       set_handle (NULL);
390     }
391   if (res)
392     res = fstat_by_name (buf);
393
394   return res;
395 }
396
397 int
398 fhandler_base::fstat_helper (struct stat *buf)
399 {
400   IO_STATUS_BLOCK st;
401   FILE_COMPRESSION_INFORMATION fci;
402   HANDLE h = get_stat_handle ();
403   PFILE_ALL_INFORMATION pfai = pc.fai ();
404   ULONG attributes = pc.file_attributes ();
405
406   to_timestruc_t (&pfai->BasicInformation.LastAccessTime, &buf->st_atim);
407   to_timestruc_t (&pfai->BasicInformation.LastWriteTime, &buf->st_mtim);
408   /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
409      (FAT for instance).  If so, it's faked using LastWriteTime. */
410   to_timestruc_t (pfai->BasicInformation.ChangeTime.QuadPart
411                   ? &pfai->BasicInformation.ChangeTime
412                   : &pfai->BasicInformation.LastWriteTime,
413                   &buf->st_ctim);
414   to_timestruc_t (&pfai->BasicInformation.CreationTime, &buf->st_birthtim);
415   buf->st_dev = get_dev ();
416   /* CV 2011-01-13: Observations on the Cygwin mailing list point to an
417      interesting behaviour in some Windows versions.  Apparently the size of
418      a directory is computed at the time the directory is first scanned.  This
419      can result in two subsequent NtQueryInformationFile calls to return size
420      0 in the first call and size > 0 in the second call.  This in turn can
421      affect applications like newer tar.
422      FIXME: Is the allocation size affected as well? */
423   buf->st_size = pc.isdir ()
424                  ? 0
425                  : (off_t) pfai->StandardInformation.EndOfFile.QuadPart;
426   /* The number of links to a directory includes the number of subdirectories
427      in the directory, since all those subdirectories point to it.  However,
428      this is painfully slow, so we do without it. */
429   buf->st_nlink = pc.fai()->StandardInformation.NumberOfLinks;
430
431   /* Enforce namehash as inode number on untrusted file systems. */
432   buf->st_ino = ino ?: get_ino ();
433
434   buf->st_blksize = PREFERRED_IO_BLKSIZE;
435
436   if (buf->st_size == 0
437       && pfai->StandardInformation.AllocationSize.QuadPart == 0LL)
438     /* File is empty and no blocks are preallocated. */
439     buf->st_blocks = 0;
440   else if (pfai->StandardInformation.AllocationSize.QuadPart > 0LL)
441     /* A successful NtQueryInformationFile returns the allocation size
442        correctly for compressed and sparse files as well.
443        Allocation size 0 is ignored here because (at least) Windows 10
444        1607 always returns 0 for CompactOS compressed files. */
445     buf->st_blocks = (pfai->StandardInformation.AllocationSize.QuadPart
446                       + S_BLKSIZE - 1) / S_BLKSIZE;
447   else if ((pfai->StandardInformation.AllocationSize.QuadPart == 0LL
448             || ::has_attribute (attributes, FILE_ATTRIBUTE_COMPRESSED
449                                           | FILE_ATTRIBUTE_SPARSE_FILE))
450            && h && !is_fs_special ()
451            && !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci,
452                                        FileCompressionInformation))
453     /* Otherwise we request the actual amount of bytes allocated for
454        compressed, sparsed and CompactOS files. */
455     buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
456                      / S_BLKSIZE;
457   else
458     /* Otherwise compute no. of blocks from file size. */
459     buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
460
461   buf->st_mode = 0;
462   /* Using a side effect: get_file_attributes checks for directory.
463      This is used, to set S_ISVTX, if needed.  */
464   if (pc.isdir ())
465     buf->st_mode = S_IFDIR;
466   else if (pc.issymlink ())
467     {
468       buf->st_size = pc.get_symlink_length ();
469       get_file_attribute (h, pc, buf->st_mode,
470                           &buf->st_uid, &buf->st_gid);
471       /* symlinks are everything for everyone! */
472       buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
473       goto done;
474     }
475   else if (pc.issocket ())
476     buf->st_mode = S_IFSOCK;
477
478   if (!get_file_attribute (h, pc, buf->st_mode, &buf->st_uid, &buf->st_gid))
479     {
480       /* If read-only attribute is set, modify ntsec return value */
481       if (::has_attribute (attributes, FILE_ATTRIBUTE_READONLY)
482           && !pc.isdir () && !pc.issymlink () && !pc.is_fs_special ())
483         buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
484
485       if (buf->st_mode & S_IFMT)
486         /* nothing */;
487       else if (!is_fs_special ())
488         buf->st_mode |= S_IFREG;
489       else
490         {
491           buf->st_dev = buf->st_rdev = dev ();
492           buf->st_mode = dev ().mode ();
493           buf->st_size = 0;
494         }
495     }
496   else
497     {
498       buf->st_mode |= STD_RBITS;
499
500       if (!::has_attribute (attributes, FILE_ATTRIBUTE_READONLY))
501         buf->st_mode |= STD_WBITS;
502       /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
503
504       if (pc.isdir ())
505         buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
506       else if (buf->st_mode & S_IFMT)
507         /* nothing */;
508       else if (is_fs_special ())
509         {
510           buf->st_dev = buf->st_rdev = dev ();
511           buf->st_mode = dev ().mode ();
512           buf->st_size = 0;
513         }
514       else
515         {
516           buf->st_mode |= S_IFREG;
517           /* Check suffix for executable file. */
518           if (pc.exec_state () != is_executable)
519             {
520               PUNICODE_STRING path = pc.get_nt_native_path ();
521
522               if (RtlEqualUnicodePathSuffix (path, &ro_u_exe, TRUE)
523                   || RtlEqualUnicodePathSuffix (path, &ro_u_lnk, TRUE))
524                 pc.set_exec ();
525             }
526           /* No known suffix, check file header.  This catches binaries and
527              shebang scripts. */
528           if (pc.exec_state () == dont_know_if_executable)
529             {
530               OBJECT_ATTRIBUTES attr;
531               NTSTATUS status = 0;
532               IO_STATUS_BLOCK io;
533
534               /* We have to re-open the file.  Either the file is not opened
535                  for reading, or the read will change the file position of the
536                  original handle. */
537               status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA,
538                                    pc.init_reopen_attr (attr, h), &io,
539                                    FILE_SHARE_VALID_FLAGS,
540                                    FILE_OPEN_FOR_BACKUP_INTENT
541                                    | FILE_SYNCHRONOUS_IO_NONALERT);
542               if (!NT_SUCCESS (status))
543                 debug_printf ("%y = NtOpenFile(%S)", status,
544                               pc.get_nt_native_path ());
545               else
546                 {
547                   LARGE_INTEGER off = { QuadPart:0LL };
548                   char magic[3];
549
550                   status = NtReadFile (h, NULL, NULL, NULL,
551                                        &io, magic, 3, &off, NULL);
552                   if (!NT_SUCCESS (status))
553                     debug_printf ("%y = NtReadFile(%S)", status,
554                                   pc.get_nt_native_path ());
555                   else if (has_exec_chars (magic, io.Information))
556                     {
557                       /* Heureka, it's an executable */
558                       pc.set_exec ();
559                       buf->st_mode |= STD_XBITS;
560                     }
561                   NtClose (h);
562                 }
563             }
564         }
565       if (pc.exec_state () == is_executable)
566         buf->st_mode |= STD_XBITS;
567
568       /* This fakes the permissions of all files to match the current umask. */
569       buf->st_mode &= ~(cygheap->umask);
570       /* If the FS supports ACLs, we're here because we couldn't even open
571          the file for READ_CONTROL access.  Chances are high that the file's
572          security descriptor has no ACE for "Everyone", so we should not fake
573          any access for "others". */
574       if (has_acls ())
575         buf->st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH);
576     }
577
578  done:
579   syscall_printf ("0 = fstat (%S, %p) st_size=%D, st_mode=0%o, st_ino=%D"
580                   "st_atim=%lx.%lx st_ctim=%lx.%lx "
581                   "st_mtim=%lx.%lx st_birthtim=%lx.%lx",
582                   pc.get_nt_native_path (), buf,
583                   buf->st_size, buf->st_mode, buf->st_ino,
584                   buf->st_atim.tv_sec, buf->st_atim.tv_nsec,
585                   buf->st_ctim.tv_sec, buf->st_ctim.tv_nsec,
586                   buf->st_mtim.tv_sec, buf->st_mtim.tv_nsec,
587                   buf->st_birthtim.tv_sec, buf->st_birthtim.tv_nsec);
588   return 0;
589 }
590
591 int
592 fhandler_disk_file::fstat (struct stat *buf)
593 {
594   return fstat_fs (buf);
595 }
596
597 int
598 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
599 {
600   int ret = -1, opened = 0;
601   IO_STATUS_BLOCK io;
602   /* We must not use the stat handle here, even if it exists.  The handle
603      has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
604      mount point, it points to the FS of the mount point, rather than to the
605      mounted FS. */
606   HANDLE fh = get_handle ();
607
608   if (!fh)
609     {
610       OBJECT_ATTRIBUTES attr;
611       opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
612                                        pc.get_object_attr (attr, sec_none_nih),
613                                        &io, FILE_SHARE_VALID_FLAGS,
614                                        FILE_OPEN_NO_RECALL
615                                        | FILE_OPEN_FOR_BACKUP_INTENT));
616       if (!opened)
617         {
618           /* Can't open file.  Try again with parent dir. */
619           UNICODE_STRING dirname;
620           RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
621           attr.ObjectName = &dirname;
622           opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
623                                            FILE_SHARE_VALID_FLAGS,
624                                            FILE_OPEN_NO_RECALL
625                                            | FILE_OPEN_FOR_BACKUP_INTENT));
626           if (!opened)
627             goto out;
628         }
629     }
630
631   ret = fstatvfs_by_handle (fh, sfs);
632 out:
633   if (opened)
634     NtClose (fh);
635   syscall_printf ("%d = fstatvfs(%s, %p)", ret, get_name (), sfs);
636   return ret;
637 }
638
639 int
640 fhandler_base::fstatvfs_by_handle (HANDLE fh, struct statvfs *sfs)
641 {
642   int ret = -1;
643   NTSTATUS status;
644   IO_STATUS_BLOCK io;
645   FILE_FS_FULL_SIZE_INFORMATION full_fsi;
646
647   sfs->f_files = ULONG_MAX;
648   sfs->f_ffree = ULONG_MAX;
649   sfs->f_favail = ULONG_MAX;
650   sfs->f_fsid = pc.fs_serial_number ();
651   sfs->f_flag = pc.fs_flags ();
652   sfs->f_namemax = pc.fs_name_len ();
653   /* Get allocation related information. */
654   status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
655                                          FileFsFullSizeInformation);
656   if (NT_SUCCESS (status))
657     {
658       sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
659       sfs->f_frsize = sfs->f_bsize;
660       sfs->f_blocks = (fsblkcnt_t) full_fsi.TotalAllocationUnits.QuadPart;
661       sfs->f_bfree = (fsblkcnt_t)
662                      full_fsi.ActualAvailableAllocationUnits.QuadPart;
663       sfs->f_bavail = (fsblkcnt_t)
664                       full_fsi.CallerAvailableAllocationUnits.QuadPart;
665       if (sfs->f_bfree > sfs->f_bavail)
666         {
667           /* Quotas active.  We can't trust TotalAllocationUnits. */
668           NTFS_VOLUME_DATA_BUFFER nvdb;
669
670           status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
671                                     FSCTL_GET_NTFS_VOLUME_DATA,
672                                     NULL, 0, &nvdb, sizeof nvdb);
673           if (!NT_SUCCESS (status))
674             debug_printf ("%y = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
675                           status, pc.get_nt_native_path ());
676           else
677             sfs->f_blocks = (fsblkcnt_t) nvdb.TotalClusters.QuadPart;
678         }
679       ret = 0;
680     }
681   else if (status == STATUS_INVALID_PARAMETER /* Netapp */
682            || status == STATUS_INVALID_INFO_CLASS)
683     {
684       FILE_FS_SIZE_INFORMATION fsi;
685       status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
686                                              FileFsSizeInformation);
687       if (NT_SUCCESS (status))
688         {
689           sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
690           sfs->f_frsize = sfs->f_bsize;
691           sfs->f_blocks = (fsblkcnt_t) fsi.TotalAllocationUnits.QuadPart;
692           sfs->f_bfree = sfs->f_bavail =
693             (fsblkcnt_t) fsi.AvailableAllocationUnits.QuadPart;
694           ret = 0;
695         }
696       else
697         debug_printf ("%y = NtQueryVolumeInformationFile"
698                       "(%S, FileFsSizeInformation)",
699                       status, pc.get_nt_native_path ());
700     }
701   else
702     debug_printf ("%y = NtQueryVolumeInformationFile"
703                   "(%S, FileFsFullSizeInformation)",
704                   status, pc.get_nt_native_path ());
705   return ret;
706 }
707
708 int
709 fhandler_disk_file::fchmod (mode_t mode)
710 {
711   int ret = -1;
712   int oret = 0;
713   NTSTATUS status;
714   IO_STATUS_BLOCK io;
715
716   if (pc.is_fs_special ()
717       /* For NFS, only handle Cygwin FIFOs specially.  Changing mode of
718          native FIFOs will work with the default code below. */
719       && (!pc.fs_is_nfs () || pc.nfsattr ()->filler1 == NF3FIFO))
720     return chmod_device (pc, mode);
721
722   if (!get_handle ())
723     {
724       query_open (query_write_dac);
725       if (!(oret = open (O_BINARY, 0)))
726         {
727           /* Need WRITE_DAC to write ACLs. */
728           if (pc.has_acls ())
729             return -1;
730           /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
731           query_open (query_write_attributes);
732           if (!(oret = open (O_BINARY, 0)))
733             return -1;
734         }
735     }
736
737   if (pc.fs_is_nfs ())
738     {
739       /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
740          Only type and mode have to be set.  Apparently type isn't checked
741          for consistency, so it's sufficent to set it to NF3REG all the time. */
742       struct {
743         FILE_FULL_EA_INFORMATION ffei;
744         char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
745       } ffei_buf;
746       ffei_buf.ffei.NextEntryOffset = 0;
747       ffei_buf.ffei.Flags = 0;
748       ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
749       ffei_buf.ffei.EaValueLength = sizeof (fattr3);
750       strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
751       fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
752                                      + ffei_buf.ffei.EaNameLength + 1);
753       memset (nfs_attr, 0, sizeof (fattr3));
754       nfs_attr->type = NF3REG;
755       nfs_attr->mode = mode;
756       status = NtSetEaFile (get_handle (), &io,
757                             &ffei_buf.ffei, sizeof ffei_buf);
758       if (!NT_SUCCESS (status))
759         __seterrno_from_nt_status (status);
760       else
761         ret = 0;
762       goto out;
763     }
764
765   if (pc.has_acls ())
766     {
767       security_descriptor sd, sd_ret;
768       uid_t uid;
769       gid_t gid;
770       tmp_pathbuf tp;
771       aclent_t *aclp;
772       bool standard_acl = false;
773       int nentries, idx;
774       mode_t attr = pc.isdir () ? S_IFDIR : 0;
775
776       if (!get_file_sd (get_handle (), pc, sd, false))
777         {
778           aclp = (aclent_t *) tp.c_get ();
779           if ((nentries = get_posix_access (sd, attr, &uid, &gid,
780                                             aclp, MAX_ACL_ENTRIES,
781                                             &standard_acl)) >= 0)
782             {
783               /* Overwrite ACL permissions as required by POSIX 1003.1e
784                  draft 17. */
785               aclp[0].a_perm = (mode >> 6) & S_IRWXO;
786
787               /* POSIXly correct: If CLASS_OBJ is present, chmod only modifies
788                  CLASS_OBJ, not GROUP_OBJ.
789
790                  Deliberate deviation from POSIX 1003.1e:  If the ACL is a
791                  "standard" ACL, that is, it only contains POSIX permissions
792                  as well as entries for the Administrators group and SYSTEM,
793                  then it's kind of a POSIX-only ACL in a twisted, Windowsy
794                  way.  If so, we change GROUP_OBJ and CLASS_OBJ perms. */
795               if (standard_acl
796                   && (idx = searchace (aclp, nentries, GROUP_OBJ)) >= 0)
797                 aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
798               if (nentries > MIN_ACL_ENTRIES
799                   && (idx = searchace (aclp, nentries, CLASS_OBJ)) >= 0)
800                 aclp[idx].a_perm = (mode >> 3) & S_IRWXO;
801
802               if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
803                 aclp[idx].a_perm = mode & S_IRWXO;
804               if (pc.isdir ())
805                 mode |= S_IFDIR;
806               if (set_posix_access (mode, uid, gid, aclp, nentries, sd_ret,
807                                     pc.fs_is_samba ()))
808                 ret = set_file_sd (get_handle (), pc, sd_ret, false);
809             }
810         }
811     }
812
813   /* If the mode has any write bits set, the DOS R/O flag is in the way. */
814   if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
815     pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
816   else if (!pc.has_acls ())     /* Never set DOS R/O if security is used. */
817     pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
818   if (S_ISSOCK (mode))
819     pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
820
821   status = NtSetAttributesFile (get_handle (), pc.file_attributes ());
822   /* MVFS needs a good amount of kicking to be convinced that it has to write
823      back metadata changes and to invalidate the cached metadata information
824      stored for the given handle.  This method to open a second handle to
825      the file and write the same metadata information twice has been found
826      experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
827   if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !oret)
828     {
829       OBJECT_ATTRIBUTES attr;
830       HANDLE fh;
831
832       if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
833                                   pc.init_reopen_attr (attr, get_handle ()),
834                                   &io, FILE_SHARE_VALID_FLAGS,
835                                   FILE_OPEN_FOR_BACKUP_INTENT)))
836         {
837           NtSetAttributesFile (fh, pc.file_attributes ());
838           NtClose (fh);
839         }
840     }
841   /* Correct NTFS security attributes have higher priority */
842   if (!pc.has_acls ())
843     {
844       if (!NT_SUCCESS (status))
845         __seterrno_from_nt_status (status);
846       else
847         ret = 0;
848     }
849
850 out:
851   if (oret)
852     close_fs ();
853
854   return ret;
855 }
856
857 int
858 fhandler_disk_file::fchown (uid_t uid, gid_t gid)
859 {
860   int oret = 0;
861   int ret = -1;
862   security_descriptor sd, sd_ret;
863   mode_t attr = pc.isdir () ? S_IFDIR : 0;
864   uid_t old_uid;
865   gid_t old_gid;
866   tmp_pathbuf tp;
867   aclent_t *aclp;
868   int nentries;
869
870   if (!pc.has_acls ())
871     {
872       /* fake - if not supported, pretend we're like win95
873          where it just works */
874       /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
875       return 0;
876     }
877
878   if (!get_handle ())
879     {
880       query_open (query_write_control);
881       if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
882         return -1;
883     }
884
885   if (get_file_sd (get_handle (), pc, sd, false))
886     goto out;
887
888   aclp = (aclent_t *) tp.c_get ();
889   if ((nentries = get_posix_access (sd, attr, &old_uid, &old_gid,
890                                     aclp, MAX_ACL_ENTRIES)) < 0)
891     goto out;
892
893   /* According to POSIX, chown can be a no-op if uid is (uid_t)-1 and
894      gid is (gid_t)-1.  Otherwise, even if uid and gid are unchanged,
895      we must ensure that ctime is updated. */
896   if (uid == ILLEGAL_UID && gid == ILLEGAL_GID)
897     {
898       ret = 0;
899       goto out;
900     }
901   if (uid == ILLEGAL_UID)
902     uid = old_uid;
903   else if (gid == ILLEGAL_GID)
904     gid = old_gid;
905
906   /* Windows ACLs can contain permissions for one group, while being owned by
907      another user/group.  The permission bits returned above are pretty much
908      useless then.  Creating a new ACL with these useless permissions results
909      in a potentially broken symlink.  So what we do here is to set the
910      underlying permissions of symlinks to a sensible value which allows the
911      world to read the symlink and only the new owner to change it. */
912   if (pc.issymlink ())
913     for (int idx = 0; idx < nentries; ++idx)
914       {
915         aclp[idx].a_perm |= S_IROTH;
916         if (aclp[idx].a_type & USER_OBJ)
917           aclp[idx].a_perm |= S_IWOTH;
918       }
919
920   if (set_posix_access (attr, uid, gid, aclp, nentries, sd_ret,
921                         pc.fs_is_samba ()))
922     ret = set_file_sd (get_handle (), pc, sd_ret, true);
923
924   /* If you're running a Samba server with no winbind, the uid<->SID mapping
925      is disfunctional.  Even trying to chown to your own account fails since
926      the account used on the server is the UNIX account which gets used for
927      the standard user mapping.  This is a default mechanism which doesn't
928      know your real Windows SID.  There are two possible error codes in
929      different Samba releases for this situation, one of them unfortunately
930      the not very significant STATUS_ACCESS_DENIED.  Instead of relying on
931      the error codes, we're using the below very simple heuristic.
932      If set_file_sd failed, and the original user account was either already
933      unknown, or one of the standard UNIX accounts, we're faking success. */
934   if (ret == -1 && pc.fs_is_samba ())
935     {
936       PSID sid;
937
938       if (uid == old_uid
939           || ((sid = sidfromuid (old_uid, NULL)) != NO_SID
940               && RtlEqualPrefixSid (sid,
941                                     well_known_samba_unix_user_fake_sid)))
942         {
943           debug_printf ("Faking chown worked on standalone Samba");
944           ret = 0;
945         }
946     }
947
948 out:
949   if (oret)
950     close_fs ();
951
952   return ret;
953 }
954
955 int
956 fhandler_disk_file::facl (int cmd, int nentries, aclent_t *aclbufp)
957 {
958   int res = -1;
959   int oret = 0;
960
961   if (!pc.has_acls ())
962     {
963 cant_access_acl:
964       switch (cmd)
965         {
966
967           case SETACL:
968             /* Open for writing required to be able to set ctime
969                (even though setting the ACL is just pretended). */
970             if (!get_handle ())
971               oret = open (O_WRONLY | O_BINARY, 0);
972             res = 0;
973             break;
974           case GETACL:
975             if (!aclbufp)
976               set_errno (EFAULT);
977             else if (nentries < MIN_ACL_ENTRIES)
978               set_errno (ENOSPC);
979             else
980               {
981                 struct stat st;
982                 if (!fstat (&st))
983                   {
984                     aclbufp[0].a_type = USER_OBJ;
985                     aclbufp[0].a_id = st.st_uid;
986                     aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
987                     aclbufp[1].a_type = GROUP_OBJ;
988                     aclbufp[1].a_id = st.st_gid;
989                     aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
990                     aclbufp[2].a_type = OTHER_OBJ;
991                     aclbufp[2].a_id = ILLEGAL_GID;
992                     aclbufp[2].a_perm = st.st_mode & S_IRWXO;
993                     res = MIN_ACL_ENTRIES;
994                   }
995               }
996             break;
997           case GETACLCNT:
998             res = MIN_ACL_ENTRIES;
999             break;
1000           default:
1001             set_errno (EINVAL);
1002             break;
1003         }
1004     }
1005   else
1006     {
1007       if ((cmd == SETACL && !get_handle ())
1008           || (cmd != SETACL && !get_stat_handle ()))
1009         {
1010           query_open (cmd == SETACL ? query_write_dac : query_read_control);
1011           if (!(oret = open (O_BINARY, 0)))
1012             {
1013               if (cmd == GETACL || cmd == GETACLCNT)
1014                 goto cant_access_acl;
1015               return -1;
1016             }
1017         }
1018       switch (cmd)
1019         {
1020           case SETACL:
1021             if (!aclsort (nentries, 0, aclbufp))
1022               {
1023                 bool rw = false;
1024                 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1025                 if (rw)
1026                   {
1027                     IO_STATUS_BLOCK io;
1028                     FILE_BASIC_INFORMATION fbi;
1029                     fbi.CreationTime.QuadPart
1030                     = fbi.LastAccessTime.QuadPart
1031                     = fbi.LastWriteTime.QuadPart
1032                     = fbi.ChangeTime.QuadPart = 0LL;
1033                     fbi.FileAttributes = (pc.file_attributes ()
1034                                           & ~FILE_ATTRIBUTE_READONLY)
1035                                          ?: FILE_ATTRIBUTE_NORMAL;
1036                     NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1037                                           FileBasicInformation);
1038                   }
1039               }
1040             break;
1041           case GETACL:
1042             if (!aclbufp)
1043               set_errno(EFAULT);
1044             else {
1045               res = getacl (get_stat_handle (), pc, nentries, aclbufp);
1046               /* For this ENOSYS case, see security.cc:get_file_attribute(). */
1047               if (res == -1 && get_errno () == ENOSYS)
1048                 goto cant_access_acl;
1049             }
1050             break;
1051           case GETACLCNT:
1052             res = getacl (get_stat_handle (), pc, 0, NULL);
1053             /* Ditto. */
1054             if (res == -1 && get_errno () == ENOSYS)
1055               goto cant_access_acl;
1056             break;
1057           default:
1058             set_errno (EINVAL);
1059             break;
1060         }
1061     }
1062
1063   if (oret)
1064     close_fs ();
1065
1066   return res;
1067 }
1068
1069 ssize_t
1070 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1071 {
1072   if (pc.is_fs_special ())
1073     {
1074       set_errno (ENOTSUP);
1075       return -1;
1076     }
1077   return read_ea (get_handle (), pc, name, (char *) value, size);
1078 }
1079
1080 int
1081 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1082                                int flags)
1083 {
1084   if (pc.is_fs_special ())
1085     {
1086       set_errno (ENOTSUP);
1087       return -1;
1088     }
1089   return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1090 }
1091
1092 int
1093 fhandler_disk_file::fadvise (off_t offset, off_t length, int advice)
1094 {
1095   if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1096     return EINVAL;
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     {
1117       fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1118       if (advice == POSIX_FADV_SEQUENTIAL)
1119         fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1120       status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1121                                      FileModeInformation);
1122       if (NT_SUCCESS (status))
1123         return 0;
1124       __seterrno_from_nt_status (status);
1125     }
1126
1127   return geterrno_from_nt_status (status);
1128 }
1129
1130 int
1131 fhandler_disk_file::falloc_allocate (int mode, off_t offset, off_t length)
1132 {
1133   NTSTATUS status;
1134   IO_STATUS_BLOCK io;
1135   FILE_STANDARD_INFORMATION fsi;
1136   FILE_END_OF_FILE_INFORMATION feofi;
1137   FILE_ALLOCATION_INFORMATION fai = { 0 };
1138
1139   /* Fetch EOF */
1140   status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1141                                    FileStandardInformation);
1142   if (!NT_SUCCESS (status))
1143     return geterrno_from_nt_status (status);
1144
1145   switch (mode)
1146     {
1147     case 0:
1148       /* For posix_fallocate(3), truncating the file is a no-op.  However,
1149          for sparse files we still have to allocate the blocks within
1150          offset and offset + length which are currently in holes, due to
1151          the following POSIX requirement:
1152          "If posix_fallocate() returns successfully, subsequent writes to
1153           the specified file data shall not fail due to the lack of free
1154           space on the file system  storage  media." */
1155       if (offset + length <= fsi.EndOfFile.QuadPart)
1156         {
1157           if (!has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1158             return 0;
1159           feofi.EndOfFile.QuadPart = fsi.EndOfFile.QuadPart;
1160         }
1161       else
1162         feofi.EndOfFile.QuadPart = offset + length;
1163       break;
1164     case __FALLOC_FL_TRUNCATE:
1165       /* For ftruncate(2), offset is 0. Just use length as is. */
1166       feofi.EndOfFile.QuadPart = length;
1167
1168       /* Make file sparse only when called through ftruncate and the mount
1169          mode supports sparse files.  Also, make sure that the new region
1170          actually spans over at least one sparsifiable chunk. */
1171       if (pc.support_sparse ()
1172           && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
1173           && span_sparse_chunk (feofi.EndOfFile.QuadPart,
1174                                 fsi.EndOfFile.QuadPart))
1175         {
1176           status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1177                                     FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1178           if (NT_SUCCESS (status))
1179             pc.file_attributes (pc.file_attributes ()
1180                                 | FILE_ATTRIBUTE_SPARSE_FILE);
1181           debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1182                         status, pc.get_nt_native_path ());
1183         }
1184       break;
1185     case FALLOC_FL_KEEP_SIZE:
1186       /* Keep track of the allocation size for overallocation below.
1187          Note that overallocation in Windows is only temporary!
1188          As soon as the last open handle to the file is closed, the
1189          overallocation gets removed by the system.  Also, overallocation
1190          for sparse files fails silently, so just don't bother. */
1191       if (offset + length > fsi.EndOfFile.QuadPart
1192           && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1193         fai.AllocationSize.QuadPart = offset + length;
1194
1195       feofi.EndOfFile.QuadPart = fsi.EndOfFile.QuadPart;
1196       break;
1197     }
1198
1199   /* Now set the new EOF */
1200   if (feofi.EndOfFile.QuadPart != fsi.EndOfFile.QuadPart)
1201     {
1202       status = NtSetInformationFile (get_handle (), &io,
1203                                      &feofi, sizeof feofi,
1204                                      FileEndOfFileInformation);
1205       if (!NT_SUCCESS (status))
1206         return geterrno_from_nt_status (status);
1207     }
1208
1209   /* If called via fallocate(2) or posix_fallocate(3), allocate blocks in
1210      sparse file holes. */
1211   if (mode != __FALLOC_FL_TRUNCATE
1212       && length
1213       && has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1214     {
1215       int res = falloc_zero_range (mode | __FALLOC_FL_ZERO_HOLES,
1216                                    offset, length);
1217       if (res)
1218         return res;
1219     }
1220
1221   /* Last but not least, set the new allocation size, if any */
1222   if (fai.AllocationSize.QuadPart)
1223     {
1224       /* This is not fatal. Just note a failure in the debug output. */
1225       status = NtSetInformationFile (get_handle (), &io,
1226                                      &fai, sizeof fai,
1227                                      FileAllocationInformation);
1228       if (!NT_SUCCESS (status))
1229         debug_printf ("%y = NtSetInformationFile(%S, "
1230                       "FileAllocationInformation)",
1231                       status, pc.get_nt_native_path ());
1232     }
1233
1234   return 0;
1235 }
1236
1237 int
1238 fhandler_disk_file::falloc_punch_hole (off_t offset, off_t length)
1239 {
1240   NTSTATUS status;
1241   IO_STATUS_BLOCK io;
1242   FILE_STANDARD_INFORMATION fsi;
1243   FILE_ZERO_DATA_INFORMATION fzi;
1244
1245   /* Fetch EOF */
1246   status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1247                                    FileStandardInformation);
1248   if (!NT_SUCCESS (status))
1249     return geterrno_from_nt_status (status);
1250
1251   if (offset > fsi.EndOfFile.QuadPart) /* no-op */
1252     return 0;
1253
1254   if (offset + length > fsi.EndOfFile.QuadPart)
1255     length = fsi.EndOfFile.QuadPart - offset;
1256
1257   /* If the file isn't sparse yet, make it so. */
1258   if (!has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1259     {
1260       status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1261                                 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1262         debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1263                       status, pc.get_nt_native_path ());
1264       if (!NT_SUCCESS (status))
1265         return geterrno_from_nt_status (status);
1266       pc.file_attributes (pc.file_attributes () | FILE_ATTRIBUTE_SPARSE_FILE);
1267     }
1268
1269   /* Now punch a hole. For once, FSCTL_SET_ZERO_DATA does it exactly as per
1270      fallocate(FALLOC_FL_PUNCH_HOLE) specs. */
1271   fzi.FileOffset.QuadPart = offset;
1272   fzi.BeyondFinalZero.QuadPart = offset + length;
1273   status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1274                             FSCTL_SET_ZERO_DATA, &fzi, sizeof fzi, NULL, 0);
1275   if (!NT_SUCCESS (status))
1276     return geterrno_from_nt_status (status);
1277
1278   return 0;
1279 }
1280
1281 int
1282 fhandler_disk_file::falloc_zero_range (int mode, off_t offset, off_t length)
1283 {
1284   NTSTATUS status;
1285   IO_STATUS_BLOCK io;
1286   FILE_STANDARD_INFORMATION fsi;
1287   FILE_ALLOCATED_RANGE_BUFFER inp, *out = NULL;
1288   OBJECT_ATTRIBUTES attr;
1289   HANDLE zo_handle;
1290   tmp_pathbuf tp;
1291   size_t data_chunk_count = 0;
1292
1293   /* Fetch EOF */
1294   status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1295                                    FileStandardInformation);
1296   if (!NT_SUCCESS (status))
1297     return geterrno_from_nt_status (status);
1298
1299   /* offset and length must not exceed EOF with FALLOC_FL_KEEP_SIZE */
1300   if (mode & FALLOC_FL_KEEP_SIZE)
1301     {
1302       if (offset > fsi.EndOfFile.QuadPart) /* no-op */
1303         return 0;
1304
1305       if (offset + length > fsi.EndOfFile.QuadPart)
1306         length = fsi.EndOfFile.QuadPart - offset;
1307     }
1308
1309   /* If the file is sparse, fetch the data ranges within the file
1310        to be able to recognize holes. */
1311   if (has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1312     {
1313       inp.FileOffset.QuadPart = offset;
1314       inp.Length.QuadPart = length;
1315       out = (FILE_ALLOCATED_RANGE_BUFFER *) tp.t_get ();
1316       status = NtFsControlFile (get_handle (), NULL, NULL, NULL,
1317                                 &io, FSCTL_QUERY_ALLOCATED_RANGES,
1318                                 &inp, sizeof inp, out, 2 * NT_MAX_PATH);
1319       if (!NT_ERROR (status))
1320         data_chunk_count = io.Information / sizeof *out;
1321     }
1322
1323   /* Re-open the file and use this handle ever after, so as not to
1324      move the file pointer of the original file object.  */
1325   status = NtOpenFile (&zo_handle, SYNCHRONIZE | GENERIC_WRITE,
1326                        pc.init_reopen_attr (attr, get_handle ()), &io,
1327                        FILE_SHARE_VALID_FLAGS, get_options ());
1328   if (!NT_SUCCESS (status))
1329     return geterrno_from_nt_status (status);
1330
1331   /* FILE_SPARSE_GRANULARITY == 2 * NT_MAX_PATH ==> fits exactly */
1332   char *nullbuf = tp.t_get ();
1333   memset (nullbuf, 0, FILE_SPARSE_GRANULARITY);
1334   int res = 0;
1335
1336   /* Split range into chunks of size FILE_SPARSE_GRANULARITY and handle
1337      them according to being data or hole */
1338   LARGE_INTEGER off = { QuadPart:offset };
1339   size_t start_idx = 0;
1340   while (length > 0)
1341     {
1342       off_t chunk_len;
1343       bool in_data = true;
1344
1345       if (off.QuadPart % FILE_SPARSE_GRANULARITY)       /* First block */
1346         chunk_len = roundup2 (off.QuadPart, FILE_SPARSE_GRANULARITY) - off.QuadPart;
1347       else
1348         chunk_len = FILE_SPARSE_GRANULARITY;
1349       if (chunk_len > length)                   /* First or last block */
1350         chunk_len = length;
1351
1352       /* Check if the current chunk is within data or hole */
1353       if (has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
1354           && off.QuadPart < fsi.EndOfFile.QuadPart)
1355         {
1356           in_data = false;
1357           for (size_t idx = start_idx; idx < data_chunk_count; ++idx)
1358             if (off.QuadPart >= out[idx].FileOffset.QuadPart)
1359               {
1360                 /* Skip entries with lower start address next time. */
1361                 start_idx = idx;
1362                 if (off.QuadPart < out[idx].FileOffset.QuadPart
1363                                    + out[idx].Length.QuadPart)
1364                   {
1365                     in_data = true;
1366                     break;
1367                   }
1368               }
1369         }
1370
1371       /* Eventually, write zeros into the block.  Completely zero out data
1372          blocks, just write a single zero to former holes in sparse files.
1373          If __FALLOC_FL_ZERO_HOLES has been specified, only write to holes. */
1374       if (!(mode & __FALLOC_FL_ZERO_HOLES) || !in_data)
1375         {
1376           status = NtWriteFile (zo_handle, NULL, NULL, NULL, &io, nullbuf,
1377                                 in_data ? chunk_len : 1, &off, NULL);
1378           if (!NT_SUCCESS (status))
1379             {
1380               res = geterrno_from_nt_status (status);
1381               break;
1382             }
1383         }
1384
1385       off.QuadPart += chunk_len;
1386       length -= chunk_len;
1387     }
1388
1389   NtClose (zo_handle);
1390   return res;
1391 }
1392
1393 int
1394 fhandler_disk_file::fallocate (int mode, off_t offset, off_t length)
1395 {
1396   if (length < 0 || !get_handle ())
1397     return EINVAL;
1398   if (pc.isdir ())
1399     return EISDIR;
1400   if (!(get_access () & GENERIC_WRITE))
1401     return EBADF;
1402
1403   switch (mode)
1404     {
1405     case 0:
1406     case __FALLOC_FL_TRUNCATE:
1407     case FALLOC_FL_KEEP_SIZE:
1408       return falloc_allocate (mode, offset, length);
1409     case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE:
1410       /* Only if the filesystem supports it... */
1411       if (!(pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES))
1412         return EOPNOTSUPP;
1413       return falloc_punch_hole (offset, length);
1414     case FALLOC_FL_ZERO_RANGE:
1415     case FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE:
1416       return falloc_zero_range (mode, offset, length);
1417     default:
1418       break;
1419     }
1420   return EINVAL;
1421 }
1422
1423 int
1424 fhandler_disk_file::link (const char *newpath)
1425 {
1426   size_t nlen = strlen (newpath);
1427   path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
1428   if (newpc.error)
1429     {
1430       set_errno (newpc.error);
1431       return -1;
1432     }
1433
1434   if (newpc.exists ())
1435     {
1436       syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1437       set_errno (EEXIST);
1438       return -1;
1439     }
1440
1441   if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
1442     {
1443       set_errno (ENOENT);
1444       return -1;
1445     }
1446
1447   char new_buf[nlen + 5];
1448   if (!newpc.error)
1449     {
1450       /* If the original file is a lnk special file,
1451          and if the original file has a .lnk suffix, add one to the hardlink
1452          as well. */
1453       if (pc.is_lnk_special ()
1454           && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1455                                         &ro_u_lnk, TRUE))
1456         {
1457           /* Shortcut hack. */
1458           stpcpy (stpcpy (new_buf, newpath), ".lnk");
1459           newpath = new_buf;
1460           newpc.check (newpath, PC_SYM_NOFOLLOW);
1461         }
1462       else if (!pc.isdir ()
1463                && pc.is_binary ()
1464                && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1465                                              &ro_u_exe, TRUE)
1466                && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1467                                               &ro_u_exe, TRUE))
1468         {
1469           /* Executable hack. */
1470           stpcpy (stpcpy (new_buf, newpath), ".exe");
1471           newpath = new_buf;
1472           newpc.check (newpath, PC_SYM_NOFOLLOW);
1473         }
1474     }
1475
1476   /* We only need READ_CONTROL access so the handle returned in pc is
1477      sufficient.  And if the file couldn't be opened with READ_CONTROL
1478      access in path_conv, we won't be able to do it here anyway. */
1479   HANDLE fh = get_stat_handle ();
1480   if (!fh)
1481     {
1482       set_errno (EACCES);
1483       return -1;
1484     }
1485   PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1486   ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1487   PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1488   pfli->ReplaceIfExists = FALSE;
1489   pfli->RootDirectory = NULL;
1490   memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1491
1492   NTSTATUS status;
1493   IO_STATUS_BLOCK io;
1494   status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1495   if (!NT_SUCCESS (status))
1496     {
1497       if (status == STATUS_INVALID_DEVICE_REQUEST
1498           || status == STATUS_NOT_SUPPORTED)
1499         /* FS doesn't support hard links.  Linux returns EPERM. */
1500         set_errno (EPERM);
1501       else
1502         __seterrno_from_nt_status (status);
1503       return -1;
1504     }
1505   else if ((pc.file_attributes () & O_TMPFILE_FILE_ATTRS)
1506            == O_TMPFILE_FILE_ATTRS)
1507     {
1508       /* An O_TMPFILE file has FILE_ATTRIBUTE_TEMPORARY and
1509          FILE_ATTRIBUTE_HIDDEN set.  After a successful hardlink the file is
1510          not temporary anymore in the usual sense.  So we remove these
1511          attributes here.
1512
1513          Note that we don't create a reopen attribute for the original
1514          link but rather a normal attribute for the just created link.
1515          The reason is a curious behaviour of Windows:  If we remove the
1516          O_TMPFILE attributes on the original link, the new link will not
1517          show up in file system listings (not even native ones from , e.g.,
1518          `cmd /c dir'), long after the original link has been closed and
1519          removed.  The file and its metadata will be kept in memory only
1520          as long as Windows sees fit.  By opening the new link, we request
1521          the attribute changes on the new link, so after closing it Windows
1522          will have an increased interest to write back the metadata. */
1523       OBJECT_ATTRIBUTES attr;
1524       status = NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1525                            newpc.get_object_attr (attr, sec_none_nih), &io,
1526                            FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
1527       if (!NT_SUCCESS (status))
1528         debug_printf ("Opening for removing TEMPORARY attrib failed, "
1529                       "status = %y", status);
1530       else
1531         {
1532           FILE_BASIC_INFORMATION fbi;
1533
1534           fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart
1535           = fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
1536           fbi.FileAttributes = (pc.file_attributes () & ~O_TMPFILE_FILE_ATTRS)
1537                                ?: FILE_ATTRIBUTE_NORMAL;
1538           status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1539                                          FileBasicInformation);
1540           if (!NT_SUCCESS (status))
1541             debug_printf ("Removing the TEMPORARY attrib failed, status = %y",
1542                           status);
1543           NtClose (fh);
1544         }
1545     }
1546   return 0;
1547 }
1548
1549 int
1550 fhandler_disk_file::utimens (const struct timespec *tvp)
1551 {
1552   return utimens_fs (tvp);
1553 }
1554
1555 int
1556 fhandler_base::utimens_fs (const struct timespec *tvp)
1557 {
1558   struct timespec timeofday;
1559   struct timespec tmp[2];
1560   bool closeit = false;
1561
1562   if (!get_handle ())
1563     {
1564       query_open (query_write_attributes);
1565       if (!open_fs (O_BINARY, 0))
1566         {
1567           /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1568              to change the timestamps.  Unfortunately it's not sufficient for a
1569              remote HPFS which requires GENERIC_WRITE, so we just retry to open
1570              for writing, though this fails for R/O files of course. */
1571           query_open (no_query);
1572           if (!open_fs (O_WRONLY | O_BINARY, 0))
1573             {
1574               syscall_printf ("Opening file failed");
1575               return -1;
1576             }
1577         }
1578       closeit = true;
1579     }
1580
1581   clock_gettime (CLOCK_REALTIME, &timeofday);
1582   if (!tvp)
1583     tmp[1] = tmp[0] = timeofday;
1584   else
1585     {
1586       if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec >= NSPERSEC)
1587           || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec >= NSPERSEC))
1588         {
1589           if (closeit)
1590             close_fs ();
1591           set_errno (EINVAL);
1592           return -1;
1593         }
1594       tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1595       tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1596     }
1597   debug_printf ("incoming lastaccess %ly %ly", tmp[0].tv_sec, tmp[0].tv_nsec);
1598
1599   IO_STATUS_BLOCK io;
1600   FILE_BASIC_INFORMATION fbi;
1601
1602   fbi.CreationTime.QuadPart = 0LL;
1603   /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1604   timespec_to_filetime (&tmp[0], &fbi.LastAccessTime);
1605   timespec_to_filetime (&tmp[1], &fbi.LastWriteTime);
1606   fbi.ChangeTime.QuadPart = 0LL;
1607   fbi.FileAttributes = 0;
1608   NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1609                                           FileBasicInformation);
1610   /* For this special case for MVFS see the comment in
1611      fhandler_disk_file::fchmod. */
1612   if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
1613     {
1614       OBJECT_ATTRIBUTES attr;
1615       HANDLE fh;
1616
1617       if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES,
1618                                   pc.init_reopen_attr (attr, get_handle ()),
1619                                   &io, FILE_SHARE_VALID_FLAGS,
1620                                   FILE_OPEN_FOR_BACKUP_INTENT)))
1621         {
1622           NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1623                                 FileBasicInformation);
1624           NtClose (fh);
1625         }
1626     }
1627   if (closeit)
1628     close_fs ();
1629   /* Opening a directory on a 9x share from a NT machine works(!), but
1630      then NtSetInformationFile fails with STATUS_NOT_SUPPORTED.  Oh well... */
1631   if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1632     {
1633       __seterrno_from_nt_status (status);
1634       return -1;
1635     }
1636   return 0;
1637 }
1638
1639 fhandler_disk_file::fhandler_disk_file () :
1640   fhandler_base (), prw_handle (NULL)
1641 {
1642 }
1643
1644 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1645   fhandler_base (), prw_handle (NULL)
1646 {
1647   set_name (pc);
1648 }
1649
1650 int
1651 fhandler_disk_file::open (int flags, mode_t mode)
1652 {
1653   return open_fs (flags, mode);
1654 }
1655
1656 int
1657 fhandler_disk_file::close (int flag)
1658 {
1659   /* Close extra pread/pwrite handle, if it exists. */
1660   if (prw_handle)
1661     NtClose (prw_handle);
1662   return fhandler_base::close ();
1663 }
1664
1665 int
1666 fhandler_disk_file::fcntl (int cmd, intptr_t arg)
1667 {
1668   int res;
1669
1670   switch (cmd)
1671     {
1672     case F_LCK_MANDATORY:       /* Mandatory locking only works on files. */
1673       mandatory_locking (!!arg);
1674       need_fork_fixup (true);
1675       res = 0;
1676       break;
1677     default:
1678       res = fhandler_base::fcntl (cmd, arg);
1679       break;
1680     }
1681   return res;
1682 }
1683
1684 int
1685 fhandler_disk_file::dup (fhandler_base *child, int flags)
1686 {
1687   fhandler_disk_file *fhc = (fhandler_disk_file *) child;
1688
1689   int ret = fhandler_base::dup (child, flags);
1690   if (!ret && prw_handle
1691       && !DuplicateHandle (GetCurrentProcess (), prw_handle,
1692                            GetCurrentProcess (), &fhc->prw_handle,
1693                            0, FALSE, DUPLICATE_SAME_ACCESS))
1694     fhc->prw_handle = NULL;
1695   return ret;
1696 }
1697
1698 void
1699 fhandler_disk_file::fixup_after_fork (HANDLE parent)
1700 {
1701   prw_handle = NULL;
1702   mandatory_locking (false);
1703   fhandler_base::fixup_after_fork (parent);
1704 }
1705
1706 int
1707 fhandler_base::open_fs (int flags, mode_t mode)
1708 {
1709   /* Unfortunately NT allows to open directories for writing, but that's
1710      disallowed according to SUSv3. */
1711   if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1712     {
1713       set_errno (EISDIR);
1714       return 0;
1715     }
1716
1717   bool new_file = !exists ();
1718
1719   int res = fhandler_base::open (flags, mode);
1720   if (res)
1721     {
1722       /* The file info in pc is wrong at this point for newly created files.
1723          Refresh it before fetching any file info. */
1724       if (new_file)
1725         pc.get_finfo (get_handle ());
1726
1727       if (pc.isgood_inode (pc.get_ino ()))
1728         ino = pc.get_ino ();
1729     }
1730
1731   syscall_printf ("%d = fhandler_disk_file::open(%S, %y)", res,
1732                   pc.get_nt_native_path (), flags);
1733   return res;
1734 }
1735
1736 /* POSIX demands that pread/pwrite don't change the current file position.
1737    While NtReadFile/NtWriteFile support atomic seek-and-io, both change the
1738    file pointer if the file handle has been opened for synchonous I/O.
1739    Using this handle for pread/pwrite would break atomicity, because the
1740    read/write operation would have to be followed by a seek back to the old
1741    file position.  What we do is to open another handle to the file on the
1742    first call to either pread or pwrite.  This is used for any subsequent
1743    pread/pwrite.  Thus the current file position of the "normal" file
1744    handle is not touched.
1745
1746    FIXME:
1747
1748    Note that this is just a hack.  The problem with this approach is that
1749    a change to the file permissions might disallow to open the file with
1750    the required permissions to read or write.  This appears to be a border case,
1751    but that's exactly what git does.  It creates the file for reading and
1752    writing and after writing it, it chmods the file to read-only.  Then it
1753    calls pread on the file to examine the content.  This works, but if git
1754    would use the original handle to pwrite to the file, it would be broken
1755    with our approach.
1756
1757    One way to implement this is to open the pread/pwrite handle right at
1758    file open time.  We would simply maintain two handles, which wouldn't
1759    be much of a problem given how we do that for other fhandler types as
1760    well.
1761
1762    However, ultimately fhandler_disk_file should become a derived class of
1763    fhandler_base_overlapped.  Each raw_read or raw_write would fetch the
1764    actual file position, read/write from there, and then set the file
1765    position again.  Fortunately, while the file position is not maintained
1766    by the I/O manager, it can be fetched and set to a new value by all
1767    processes holding a handle to that file object.  Pread and pwrite differ
1768    from raw_read and raw_write just by not touching the current file pos.
1769    Actually they could be merged with raw_read/raw_write if we add a position
1770    parameter to the latter. */
1771
1772 int
1773 fhandler_disk_file::prw_open (bool write, void *aio)
1774 {
1775   NTSTATUS status;
1776   IO_STATUS_BLOCK io;
1777   OBJECT_ATTRIBUTES attr;
1778   ULONG options = get_options ();
1779
1780   /* If async i/o is intended, turn off the default synchronous operation */
1781   if (aio)
1782     options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
1783
1784   /* First try to open with the original access mask */
1785   ACCESS_MASK access = get_access ();
1786   status = NtOpenFile (&prw_handle, access,
1787                        pc.init_reopen_attr (attr, get_handle ()), &io,
1788                        FILE_SHARE_VALID_FLAGS, options);
1789   if (status == STATUS_ACCESS_DENIED)
1790     {
1791       /* If we get an access denied, chmod has been called.  Try again
1792          with just the required rights to perform the called function. */
1793       access &= write ? ~GENERIC_READ : ~GENERIC_WRITE;
1794       status = NtOpenFile (&prw_handle, access, &attr, &io,
1795                            FILE_SHARE_VALID_FLAGS, options);
1796     }
1797   debug_printf ("%y = NtOpenFile (%p, %y, %S, io, %y, %y)",
1798                 status, prw_handle, access, pc.get_nt_native_path (),
1799                 FILE_SHARE_VALID_FLAGS, options);
1800   if (!NT_SUCCESS (status))
1801     {
1802       __seterrno_from_nt_status (status);
1803       return -1;
1804     }
1805
1806   /* prw_handle is invalid after fork. */
1807   need_fork_fixup (true);
1808
1809   /* record prw_handle's asyncness for subsequent pread/pwrite operations */
1810   prw_handle_isasync = !!aio;
1811   return 0;
1812 }
1813
1814 ssize_t
1815 fhandler_disk_file::pread (void *buf, size_t count, off_t offset, void *aio)
1816 {
1817   struct aiocb *aiocb = (struct aiocb *) aio;
1818   ssize_t res;
1819
1820   if ((get_flags () & O_ACCMODE) == O_WRONLY)
1821     {
1822       set_errno (EBADF);
1823       return -1;
1824     }
1825
1826   /* In binary mode, we can use an atomic NtReadFile call.
1827      Windows mandatory locking semantics disallow to use another HANDLE. */
1828   if (rbinary () && !mandatory_locking ())
1829     {
1830       extern int is_at_eof (HANDLE h);
1831       NTSTATUS status;
1832       IO_STATUS_BLOCK io;
1833       LARGE_INTEGER off = { QuadPart:offset };
1834       HANDLE evt = aio ? (HANDLE) aiocb->aio_wincb.event : NULL;
1835       PIO_STATUS_BLOCK pio = aio ? (PIO_STATUS_BLOCK) &aiocb->aio_wincb : &io;
1836
1837       /* If existing prw_handle asyncness doesn't match this call's, re-open */
1838       if (prw_handle && (prw_handle_isasync != !!aio))
1839         NtClose (prw_handle), prw_handle = NULL;
1840
1841       if (!prw_handle && prw_open (false, aio))
1842         goto non_atomic;
1843       status = NtReadFile (prw_handle, evt, NULL, NULL, pio, buf, count,
1844                            &off, NULL);
1845       if (status == STATUS_END_OF_FILE)
1846         res = 0;
1847       else if (!NT_SUCCESS (status))
1848         {
1849           if (pc.isdir ())
1850             {
1851               set_errno (EISDIR);
1852               return -1;
1853             }
1854           if (status == (NTSTATUS) STATUS_ACCESS_VIOLATION)
1855             {
1856               if (is_at_eof (prw_handle))
1857                 {
1858                   res = 0;
1859                   goto out;
1860                 }
1861               switch (mmap_is_attached_or_noreserve (buf, count))
1862                 {
1863                 case MMAP_NORESERVE_COMMITED:
1864                   status = NtReadFile (prw_handle, evt, NULL, NULL, pio,
1865                                        buf, count, &off, NULL);
1866                   if (NT_SUCCESS (status))
1867                     {
1868                       res = aio ? (ssize_t) aiocb->aio_wincb.info
1869                                 : io.Information;
1870                       goto out;
1871                     }
1872                   break;
1873                 case MMAP_RAISE_SIGBUS:
1874                   raise (SIGBUS);
1875                 default:
1876                   break;
1877                 }
1878             }
1879           __seterrno_from_nt_status (status);
1880           return -1;
1881         }
1882       else
1883         {
1884           res = aio ? (ssize_t) aiocb->aio_wincb.info : io.Information;
1885           goto out;
1886         }
1887     }
1888   else
1889     {
1890 non_atomic:
1891       /* Text mode stays slow and non-atomic. */
1892       off_t curpos = lseek (0, SEEK_CUR);
1893       if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1894         res = -1;
1895       else
1896         {
1897           size_t tmp_count = count;
1898           read (buf, tmp_count);
1899           if (lseek (curpos, SEEK_SET) >= 0)
1900             res = (ssize_t) tmp_count;
1901           else
1902             res = -1;
1903         }
1904
1905       /* If this was a disallowed async request, simulate its conclusion */
1906       if (aio)
1907         {
1908           aiocb->aio_rbytes = res;
1909           aiocb->aio_errno = res == -1 ? get_errno () : 0;
1910           SetEvent ((HANDLE) aiocb->aio_wincb.event);
1911         }
1912     }
1913 out:
1914   debug_printf ("%d = pread(%p, %ld, %D, %p)\n", res, buf, count, offset, aio);
1915   return res;
1916 }
1917
1918 ssize_t
1919 fhandler_disk_file::pwrite (void *buf, size_t count, off_t offset, void *aio)
1920 {
1921   struct aiocb *aiocb = (struct aiocb *) aio;
1922   ssize_t res;
1923
1924   if ((get_flags () & O_ACCMODE) == O_RDONLY)
1925     {
1926       set_errno (EBADF);
1927       return -1;
1928     }
1929
1930   /* In binary mode, we can use an atomic NtWriteFile call.
1931      Windows mandatory locking semantics disallow to use another HANDLE. */
1932   if (wbinary () && !mandatory_locking ())
1933     {
1934       NTSTATUS status;
1935       IO_STATUS_BLOCK io;
1936       FILE_STANDARD_INFORMATION fsi;
1937       LARGE_INTEGER off = { QuadPart:offset };
1938       HANDLE evt = aio ? (HANDLE) aiocb->aio_wincb.event : NULL;
1939       PIO_STATUS_BLOCK pio = aio ? (PIO_STATUS_BLOCK) &aiocb->aio_wincb : &io;
1940
1941       /* If existing prw_handle asyncness doesn't match this call's, re-open */
1942       if (prw_handle && (prw_handle_isasync != !!aio))
1943         NtClose (prw_handle), prw_handle = NULL;
1944
1945       /* If the file system supports sparse files and the application is
1946          writing beyond EOF spanning more than one sparsifiable chunk,
1947          convert the file to a sparse file. */
1948       if (pc.support_sparse ()
1949           && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE)
1950           && NT_SUCCESS (NtQueryInformationFile (get_handle (),
1951                                                  &io, &fsi, sizeof fsi,
1952                                                  FileStandardInformation))
1953           && span_sparse_chunk (offset, fsi.EndOfFile.QuadPart))
1954         {
1955           NTSTATUS status;
1956           status = NtFsControlFile (get_handle (), NULL, NULL, NULL,
1957                                     &io, FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1958           if (NT_SUCCESS (status))
1959             pc.file_attributes (pc.file_attributes ()
1960                                 | FILE_ATTRIBUTE_SPARSE_FILE);
1961           debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1962                         status, pc.get_nt_native_path ());
1963         }
1964       if (!prw_handle && prw_open (true, aio))
1965         goto non_atomic;
1966       status = NtWriteFile (prw_handle, evt, NULL, NULL, pio, buf, count,
1967                             &off, NULL);
1968       if (!NT_SUCCESS (status))
1969         {
1970           __seterrno_from_nt_status (status);
1971           return -1;
1972         }
1973       res = aio ? (ssize_t) aiocb->aio_wincb.info : io.Information;
1974       goto out;
1975     }
1976   else
1977     {
1978 non_atomic:
1979       /* Text mode stays slow and non-atomic. */
1980       off_t curpos = lseek (0, SEEK_CUR);
1981       if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1982         res = curpos;
1983       else
1984         {
1985           res = (ssize_t) write (buf, count);
1986           if (lseek (curpos, SEEK_SET) < 0)
1987             res = -1;
1988         }
1989
1990       /* If this was a disallowed async request, simulate its conclusion */
1991       if (aio)
1992         {
1993           aiocb->aio_rbytes = res;
1994           aiocb->aio_errno = res == -1 ? get_errno () : 0;
1995           SetEvent ((HANDLE) aiocb->aio_wincb.event);
1996         }
1997     }
1998 out:
1999   debug_printf ("%d = pwrite(%p, %ld, %D, %p)\n", res, buf, count, offset, aio);
2000   return res;
2001 }
2002
2003 int
2004 fhandler_disk_file::mkdir (mode_t mode)
2005 {
2006   int res = -1;
2007   SECURITY_ATTRIBUTES sa = sec_none_nih;
2008   NTSTATUS status;
2009   HANDLE dir;
2010   OBJECT_ATTRIBUTES attr;
2011   IO_STATUS_BLOCK io;
2012   PFILE_FULL_EA_INFORMATION p = NULL;
2013   ULONG plen = 0;
2014   ULONG access = FILE_LIST_DIRECTORY | SYNCHRONIZE;
2015
2016   if (pc.fs_is_nfs ())
2017     {
2018       /* When creating a dir on an NFS share, we have to set the
2019          file mode by writing a NFS fattr3 structure with the
2020          correct mode bits set. */
2021       plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
2022              + sizeof (fattr3);
2023       p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
2024       p->NextEntryOffset = 0;
2025       p->Flags = 0;
2026       p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
2027       p->EaValueLength = sizeof (fattr3);
2028       strcpy (p->EaName, NFS_V3_ATTR);
2029       fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
2030       memset (nfs_attr, 0, sizeof (fattr3));
2031       nfs_attr->type = NF3DIR;
2032       nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
2033     }
2034   else if (has_acls () && !isremote ())
2035     /* If the filesystem supports ACLs, we will overwrite the DACL after the
2036        call to NtCreateFile.  This requires a handle with READ_CONTROL and
2037        WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
2038        open the file again.
2039        FIXME: On remote NTFS shares open sometimes fails because even the
2040        creator of the file doesn't have the right to change the DACL.
2041        I don't know what setting that is or how to recognize such a share,
2042        so for now we don't request WRITE_DAC on remote drives. */
2043     access |= READ_CONTROL | WRITE_DAC;
2044   /* Setting case sensitivity requires FILE_WRITE_ATTRIBUTES. */
2045   if (wincap.has_case_sensitive_dirs ()
2046       && !pc.isremote () && pc.fs_is_ntfs ())
2047     access |= FILE_WRITE_ATTRIBUTES;
2048   status = NtCreateFile (&dir, access, pc.get_object_attr (attr, sa), &io, NULL,
2049                          FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
2050                          FILE_CREATE,
2051                          FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
2052                          | FILE_OPEN_FOR_BACKUP_INTENT,
2053                          p, plen);
2054   if (NT_SUCCESS (status))
2055     {
2056       /* Set the "directory attribute" so that pc.isdir() returns correct
2057          value in subsequent function calls. */
2058       pc.file_attributes (FILE_ATTRIBUTE_DIRECTORY);
2059       if (has_acls ())
2060         set_created_file_access (dir, pc, mode & 07777);
2061 #if 0
2062       /* FIXME: This default behaviour badly breaks interoperability.
2063                 Inspecting the content of case-sensitive directories
2064                 on remote machines results in lots of errors like
2065                 disappearing diretories and files, file not found, etc. */
2066
2067       /* Starting with Windows 10 1803, try to create all dirs below the
2068          installation root as case-sensitive.  If STATUS_NOT_SUPPORTED
2069          is returned, WSL isn't installed (unfortunately a requirement
2070          for this functionality. */
2071       if (wincap.has_case_sensitive_dirs ()
2072           && !pc.isremote () && pc.fs_is_ntfs ())
2073         {
2074           PUNICODE_STRING new_dir = pc.get_nt_native_path ();
2075           PUNICODE_STRING root_dir = &cygheap->installation_root;
2076
2077           if (RtlEqualUnicodePathPrefix (new_dir, root_dir, TRUE)
2078               && new_dir->Buffer[root_dir->Length / sizeof (WCHAR)] == L'\\')
2079             {
2080               FILE_CASE_SENSITIVE_INFORMATION fcsi;
2081
2082               fcsi.Flags = FILE_CS_FLAG_CASE_SENSITIVE_DIR;
2083               status = NtSetInformationFile (dir, &io, &fcsi, sizeof fcsi,
2084                                              FileCaseSensitiveInformation);
2085               if (!NT_SUCCESS (status))
2086                 {
2087                   debug_printf ("Setting dir case sensitivity, status %y",
2088                                 status);
2089                   if (status == STATUS_NOT_SUPPORTED)
2090                     {
2091                       debug_printf ("Dir case sensitivity requires WSL");
2092                       wincap.disable_case_sensitive_dirs ();
2093                     }
2094                 }
2095             }
2096         }
2097 #endif
2098       NtClose (dir);
2099       res = 0;
2100     }
2101   else
2102     __seterrno_from_nt_status (status);
2103
2104   return res;
2105 }
2106
2107 int
2108 fhandler_disk_file::rmdir ()
2109 {
2110   if (!pc.isdir ())
2111     {
2112       set_errno (ENOTDIR);
2113       return -1;
2114     }
2115   if (!pc.exists ())
2116     {
2117       set_errno (ENOENT);
2118       return -1;
2119     }
2120
2121   NTSTATUS status = unlink_nt (pc, false);
2122
2123   if (!NT_SUCCESS (status))
2124     {
2125       __seterrno_from_nt_status (status);
2126       return -1;
2127     }
2128   return 0;
2129 }
2130
2131 /* This is the minimal number of entries which fit into the readdir cache.
2132    The number of bytes allocated by the cache is determined by this number,
2133    To tune caching, just tweak this number.  To get a feeling for the size,
2134    the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes.  */
2135
2136 #define DIR_NUM_ENTRIES 100             /* Cache size 62404 bytes */
2137
2138 #define DIR_BUF_SIZE    (DIR_NUM_ENTRIES \
2139                          * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
2140                             + (NAME_MAX + 1) * sizeof (WCHAR)))
2141
2142 struct __DIR_cache
2143 {
2144   char  __cache[DIR_BUF_SIZE];
2145   ULONG __pos;
2146 };
2147
2148 #define d_cachepos(d)   (((__DIR_cache *) (d)->__d_dirname)->__pos)
2149 #define d_cache(d)      (((__DIR_cache *) (d)->__d_dirname)->__cache)
2150
2151 #define d_mounts(d)     ((__DIR_mounts *) (d)->__d_internal)
2152
2153 DIR *
2154 fhandler_disk_file::opendir (int fd)
2155 {
2156   DIR *dir;
2157   DIR *res = NULL;
2158
2159   if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
2160     set_errno (ENOMEM);
2161   else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
2162            == NULL)
2163     {
2164       set_errno (ENOMEM);
2165       goto free_dir;
2166     }
2167   else if ((dir->__d_dirent =
2168             (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
2169     {
2170       set_errno (ENOMEM);
2171       goto free_dirname;
2172     }
2173   else
2174     {
2175       cygheap_fdnew cfd;
2176       if (cfd < 0 && fd < 0)
2177         goto free_dirent;
2178
2179       dir->__d_dirent->__d_version = __DIRENT_VERSION;
2180       dir->__d_cookie = __DIRENT_COOKIE;
2181       dir->__handle = INVALID_HANDLE_VALUE;
2182       dir->__d_position = 0;
2183       dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
2184                      ? dirent_isroot : 0;
2185       dir->__d_internal = 0;
2186
2187       if (pc.iscygdrive ())
2188         {
2189           if (fd < 0 && !open (O_RDONLY, 0))
2190             goto free_mounts;
2191         }
2192       else
2193         {
2194           dir->__d_internal = (uintptr_t) new __DIR_mounts (get_name ());
2195           d_cachepos (dir) = 0;
2196           if (fd < 0)
2197             {
2198               /* opendir() case.  Initialize with given directory name and
2199                  NULL directory handle. */
2200               OBJECT_ATTRIBUTES attr;
2201               NTSTATUS status;
2202               IO_STATUS_BLOCK io;
2203               /* Tools like ls(1) call dirfd() to fetch the directory
2204                  descriptor for calls to facl or fstat.  The tight access mask
2205                  used so far is not sufficient to reuse the handle for these
2206                  calls, instead the facl/fstat calls find the handle to be
2207                  unusable and have to re-open the file for reading attributes
2208                  and control data.  So, what we do here is to try to open the
2209                  directory with more relaxed access mask which enables to use
2210                  the handle for the aforementioned purpose.  This should work
2211                  in almost all cases.  Only if it doesn't work due to
2212                  permission problems, we drop the additional access bits and
2213                  try again. */
2214               ACCESS_MASK fstat_mask = READ_CONTROL | FILE_READ_ATTRIBUTES;
2215
2216               do
2217                 {
2218                   status = NtOpenFile (&get_handle (),
2219                                        SYNCHRONIZE | FILE_LIST_DIRECTORY
2220                                        | fstat_mask,
2221                                        pc.get_object_attr (attr, sec_none_nih),
2222                                        &io, FILE_SHARE_VALID_FLAGS,
2223                                        FILE_SYNCHRONOUS_IO_NONALERT
2224                                        | FILE_OPEN_FOR_BACKUP_INTENT
2225                                        | FILE_DIRECTORY_FILE);
2226                   if (!NT_SUCCESS (status))
2227                     {
2228                       if (status == STATUS_ACCESS_DENIED && fstat_mask)
2229                         fstat_mask = 0;
2230                       else
2231                         {
2232                           __seterrno_from_nt_status (status);
2233                           goto free_mounts;
2234                         }
2235                     }
2236                 }
2237               while (!NT_SUCCESS (status));
2238             }
2239
2240           /* FileIdBothDirectoryInformation was unsupported on XP when
2241              accessing UDF.  It's not clear if the call isn't also unsupported
2242              on other OS/FS combinations.  Instead of testing for yet another
2243              error code, use FileIdBothDirectoryInformation only on FSes
2244              supporting persistent ACLs.
2245
2246              NFS clients hide dangling symlinks from directory queries,
2247              unless you use the FileNamesInformation info class.
2248              FileIdBothDirectoryInformation works fine, but only if the NFS
2249              share is mounted to a drive letter.  TODO: We don't test that
2250              here for now, but it might be worth to test if there's a speed
2251              gain in using FileIdBothDirectoryInformation, because it doesn't
2252              require to open the file to read the inode number. */
2253           if (pc.hasgood_inode ())
2254             {
2255               dir->__flags |= dirent_set_d_ino;
2256               if (pc.fs_is_nfs ())
2257                 dir->__flags |= dirent_nfs_d_ino;
2258               else if (!pc.has_buggy_fileid_dirinfo ())
2259                 dir->__flags |= dirent_get_d_ino;
2260             }
2261         }
2262       if (fd >= 0)
2263         dir->__d_fd = fd;
2264       else
2265         {
2266           /* Filling cfd with `this' (aka storing this in the file
2267              descriptor table should only happen after it's clear that
2268              opendir doesn't fail, otherwise we end up cfree'ing the
2269              fhandler twice, once in opendir() in dir.cc, the second
2270              time on exit.  Nasty, nasty... */
2271           cfd = this;
2272           dir->__d_fd = cfd;
2273         }
2274       set_close_on_exec (true);
2275       dir->__fh = this;
2276       res = dir;
2277     }
2278
2279   syscall_printf ("%p = opendir (%s)", res, get_name ());
2280   return res;
2281
2282 free_mounts:
2283   delete d_mounts (dir);
2284 free_dirent:
2285   free (dir->__d_dirent);
2286 free_dirname:
2287   free (dir->__d_dirname);
2288 free_dir:
2289   free (dir);
2290   return res;
2291 }
2292
2293 ino_t
2294 readdir_get_ino (const char *path, bool dot_dot)
2295 {
2296   char *fname;
2297   struct stat st;
2298   HANDLE hdl;
2299   OBJECT_ATTRIBUTES attr;
2300   IO_STATUS_BLOCK io;
2301   ino_t ino = 0;
2302
2303   if (dot_dot)
2304     {
2305       fname = (char *) alloca (strlen (path) + 4);
2306       char *c = stpcpy (fname, path);
2307       if (c[-1] != '/')
2308         *c++ = '/';
2309       strcpy (c, "..");
2310       path = fname;
2311     }
2312   path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE);
2313   if (pc.isspecial ())
2314     {
2315       if (!stat_worker (pc, &st))
2316         ino = st.st_ino;
2317     }
2318   else if (!pc.hasgood_inode ())
2319     ino = hash_path_name (0, pc.get_nt_native_path ());
2320   else if ((hdl = pc.handle ()) != NULL
2321            || NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
2322                                       pc.get_object_attr (attr, sec_none_nih),
2323                                       &io, FILE_SHARE_VALID_FLAGS,
2324                                       FILE_OPEN_NO_RECALL
2325                                       | FILE_OPEN_FOR_BACKUP_INTENT
2326                                       | (pc.is_known_reparse_point ()
2327                                       ? FILE_OPEN_REPARSE_POINT : 0)))
2328           )
2329     {
2330       ino = pc.get_ino_by_handle (hdl);
2331       if (!ino)
2332         ino = hash_path_name (0, pc.get_nt_native_path ());
2333     }
2334   return ino;
2335 }
2336
2337 int
2338 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
2339                                     DWORD attr, PUNICODE_STRING fname)
2340 {
2341   if (w32_err)
2342     {
2343       switch (d_mounts (dir)->check_missing_mount (fname))
2344         {
2345         case __DIR_mount_none:
2346           fname->Length = 0;
2347           return geterrno_from_win_error (w32_err);
2348         case __DIR_mount_virt_target:
2349           de->d_type = DT_DIR;
2350           break;
2351         default:
2352           break;
2353         }
2354       attr = 0;
2355       dir->__flags &= ~dirent_set_d_ino;
2356     }
2357
2358   /* Set d_type if type can be determined from file attributes.  For .lnk
2359      symlinks, d_type will be reset below.  Reparse points can be NTFS
2360      symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
2361   if (attr && !(attr & ~FILE_ATTRIBUTE_VALID_FLAGS))
2362     {
2363       if (attr & FILE_ATTRIBUTE_DIRECTORY)
2364         de->d_type = DT_DIR;
2365       /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
2366       else if (!(attr & FILE_ATTRIBUTE_SYSTEM))
2367         de->d_type = DT_REG;
2368     }
2369
2370   /* Check for reparse points that can be treated as posix symlinks.
2371      Mountpoints and unknown or unhandled reparse points will be treated
2372      as normal file/directory/unknown. In all cases, returning the INO of
2373      the reparse point (not of the target) matches behavior of posix systems.
2374      Unless the file is OFFLINE. *.
2375      */
2376   if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) && !isoffline (attr))
2377     {
2378       OBJECT_ATTRIBUTES oattr;
2379
2380       InitializeObjectAttributes (&oattr, fname, pc.objcaseinsensitive (),
2381                                   get_handle (), NULL);
2382       if (readdir_check_reparse_point (&oattr, isremote ()))
2383         de->d_type = DT_LNK;
2384     }
2385
2386   /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
2387      .lnk suffix and set d_type accordingly. */
2388   if ((attr & (FILE_ATTRIBUTE_DIRECTORY
2389                | FILE_ATTRIBUTE_REPARSE_POINT
2390                | FILE_ATTRIBUTE_READONLY)) == FILE_ATTRIBUTE_READONLY
2391       && fname->Length > 4 * sizeof (WCHAR))
2392     {
2393       UNICODE_STRING uname;
2394
2395       RtlInitCountedUnicodeString (&uname,
2396                                    fname->Buffer
2397                                    + fname->Length / sizeof (WCHAR) - 4,
2398                                    4 * sizeof (WCHAR));
2399       if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
2400         {
2401           tmp_pathbuf tp;
2402           char *file = tp.c_get ();
2403           char *p = stpcpy (file, pc.get_posix ());
2404           if (p[-1] != '/')
2405             *p++ = '/';
2406           sys_wcstombs (p, NT_MAX_PATH - (p - file),
2407                         fname->Buffer, fname->Length / sizeof (WCHAR));
2408           path_conv fpath (file, PC_SYM_NOFOLLOW);
2409           if (fpath.issymlink ())
2410             {
2411               fname->Length -= 4 * sizeof (WCHAR);
2412               de->d_type = DT_LNK;
2413             }
2414           else if (fpath.isfifo ())
2415             {
2416               fname->Length -= 4 * sizeof (WCHAR);
2417               de->d_type = DT_FIFO;
2418             }
2419           else if (fpath.is_fs_special ())
2420             {
2421               fname->Length -= 4 * sizeof (WCHAR);
2422               de->d_type = S_ISCHR (fpath.dev.mode ()) ? DT_CHR : DT_BLK;
2423             }
2424         }
2425     }
2426
2427   sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
2428                 fname->Length / sizeof (WCHAR));
2429
2430   /* Don't try to optimize relative to dir->__d_position.  On several
2431      filesystems it's no safe bet that "." and ".." entries always
2432      come first. */
2433   if (de->d_name[0] == '.')
2434     {
2435       if (de->d_name[1] == '\0')
2436         dir->__flags |= dirent_saw_dot;
2437       else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
2438         dir->__flags |= dirent_saw_dot_dot;
2439     }
2440   return 0;
2441 }
2442
2443 int
2444 fhandler_disk_file::readdir (DIR *dir, dirent *de)
2445 {
2446   int res = 0;
2447   NTSTATUS status = STATUS_SUCCESS;
2448   PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
2449   PWCHAR FileName;
2450   ULONG FileNameLength;
2451   ULONG FileAttributes;
2452   IO_STATUS_BLOCK io;
2453   UNICODE_STRING fname;
2454
2455   /* d_cachepos always refers to the next cache entry to use.  If it's 0
2456      we must reload the cache. */
2457   FileAttributes = 0;
2458   if (d_cachepos (dir) == 0)
2459     {
2460       if ((dir->__flags & dirent_get_d_ino))
2461         {
2462           status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2463                                          d_cache (dir), DIR_BUF_SIZE,
2464                                          FileIdBothDirectoryInformation,
2465                                          FALSE, NULL, dir->__d_position == 0);
2466           /* FileIdBothDirectoryInformation isn't supported on some
2467              remote drives, but we don't know every system out there.
2468              Check various status codes indicating this. */
2469           if (!NT_SUCCESS (status)
2470               && (status == STATUS_INVALID_LEVEL
2471                   || status == STATUS_NOT_SUPPORTED
2472                   || status == STATUS_INVALID_PARAMETER
2473                   || status == STATUS_INVALID_NETWORK_RESPONSE
2474                   || status == STATUS_INVALID_INFO_CLASS))
2475             dir->__flags &= ~dirent_get_d_ino;
2476         }
2477       /* NFS must use FileNamesInformation!  Any other information class
2478          skips all symlinks. */
2479       if (!(dir->__flags & dirent_get_d_ino))
2480         status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2481                                        d_cache (dir), DIR_BUF_SIZE,
2482                                        (dir->__flags & dirent_nfs_d_ino)
2483                                        ? FileNamesInformation
2484                                        : FileBothDirectoryInformation,
2485                                        FALSE, NULL, dir->__d_position == 0);
2486     }
2487
2488   if (status == STATUS_NO_MORE_FILES)
2489     /*nothing*/;
2490   else if (!NT_SUCCESS (status))
2491     debug_printf ("NtQueryDirectoryFile failed, status %y, win32 error %u",
2492                   status, RtlNtStatusToDosError (status));
2493   else
2494     {
2495       buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
2496       if (buf->NextEntryOffset == 0)
2497         d_cachepos (dir) = 0;
2498       else
2499         d_cachepos (dir) += buf->NextEntryOffset;
2500       if ((dir->__flags & dirent_get_d_ino))
2501         {
2502           FileName = buf->FileName;
2503           FileNameLength = buf->FileNameLength;
2504           FileAttributes = buf->FileAttributes;
2505           if ((dir->__flags & dirent_set_d_ino))
2506             de->d_ino = buf->FileId.QuadPart;
2507         }
2508       else if ((dir->__flags & dirent_nfs_d_ino))
2509         {
2510           FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
2511           FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
2512         }
2513       else
2514         {
2515           FileName = ((PFILE_BOTH_DIR_INFORMATION) buf)->FileName;
2516           FileNameLength =
2517                 ((PFILE_BOTH_DIR_INFORMATION) buf)->FileNameLength;
2518           FileAttributes =
2519                 ((PFILE_BOTH_DIR_INFORMATION) buf)->FileAttributes;
2520         }
2521       RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
2522       d_mounts (dir)->check_mount (&fname);
2523       if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
2524         {
2525           /* Don't try to optimize relative to dir->__d_position.  On several
2526              filesystems it's no safe bet that "." and ".." entries always
2527              come first. */
2528           if (FileNameLength == sizeof (WCHAR) && FileName[0] == '.')
2529             de->d_ino = pc.get_ino_by_handle (get_handle ());
2530           else if (FileNameLength == 2 * sizeof (WCHAR)
2531                    && FileName[0] == L'.' && FileName[1] == L'.')
2532             {
2533               if (!(dir->__flags & dirent_isroot))
2534                 de->d_ino = readdir_get_ino (get_name (), true);
2535               else
2536                 de->d_ino = pc.get_ino_by_handle (get_handle ());
2537             }
2538           else
2539             {
2540               OBJECT_ATTRIBUTES attr;
2541               HANDLE hdl;
2542               NTSTATUS f_status;
2543
2544               InitializeObjectAttributes (&attr, &fname,
2545                                           pc.objcaseinsensitive (),
2546                                           get_handle (), NULL);
2547               /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2548                  NtOpenFile here returns the inode number of the symlink target,
2549                  rather than the inode number of the symlink itself.
2550
2551                  Worse, trying to open a symlink without setting the special
2552                  "ActOnSymlink" EA triggers a bug in Windows 7 which results
2553                  in a timeout of up to 20 seconds, followed by two exceptions
2554                  in the NT kernel.
2555
2556                  Since both results are far from desirable, we open symlinks
2557                  on NFS so that we get the right inode and a happy W7.
2558                  And, since some filesystems choke on the EAs, we don't
2559                  use them unconditionally. */
2560               f_status = (dir->__flags & dirent_nfs_d_ino)
2561                          ? NtCreateFile (&hdl,
2562                                          READ_CONTROL | FILE_READ_ATTRIBUTES,
2563                                          &attr, &io, NULL, 0,
2564                                          FILE_SHARE_VALID_FLAGS, FILE_OPEN,
2565                                          FILE_OPEN_FOR_BACKUP_INTENT,
2566                                          &nfs_aol_ffei, sizeof nfs_aol_ffei)
2567                          : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
2568                                        FILE_SHARE_VALID_FLAGS,
2569                                        FILE_OPEN_NO_RECALL
2570                                        | FILE_OPEN_FOR_BACKUP_INTENT
2571                                        | FILE_OPEN_REPARSE_POINT);
2572               if (NT_SUCCESS (f_status))
2573                 {
2574                   /* We call NtQueryInformationFile here, rather than
2575                      pc.get_ino_by_handle(), otherwise we can't short-circuit
2576                      dirent_set_d_ino correctly. */
2577                   FILE_INTERNAL_INFORMATION fii;
2578                   f_status = NtQueryInformationFile (hdl, &io, &fii, sizeof fii,
2579                                                      FileInternalInformation);
2580                   /* On NFS fetch the (faked, but useful) DOS attribute.
2581                      We need it to recognize shortcut FIFOs. */
2582                   if ((dir->__flags & dirent_nfs_d_ino))
2583                     {
2584                       FILE_BASIC_INFORMATION fbi;
2585
2586                       if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fbi,
2587                                       sizeof fbi, FileBasicInformation)))
2588                         FileAttributes = fbi.FileAttributes;
2589                     }
2590                   NtClose (hdl);
2591                   if (NT_SUCCESS (f_status))
2592                     {
2593                       if (pc.isgood_inode (fii.IndexNumber.QuadPart))
2594                         de->d_ino = fii.IndexNumber.QuadPart;
2595                       else
2596                         /* Untrusted file system.  Don't try to fetch inode
2597                            number again. */
2598                         dir->__flags &= ~dirent_set_d_ino;
2599                     }
2600                 }
2601             }
2602         }
2603     }
2604
2605   if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
2606                               FileAttributes, &fname)))
2607     dir->__d_position++;
2608   else if (!(dir->__flags & dirent_saw_dot))
2609     {
2610       strcpy (de->d_name , ".");
2611       de->d_ino = pc.get_ino_by_handle (get_handle ());
2612       de->d_type = DT_DIR;
2613       dir->__d_position++;
2614       dir->__flags |= dirent_saw_dot;
2615       res = 0;
2616     }
2617   else if (!(dir->__flags & dirent_saw_dot_dot))
2618     {
2619       strcpy (de->d_name , "..");
2620       if (!(dir->__flags & dirent_isroot))
2621         de->d_ino = readdir_get_ino (get_name (), true);
2622       else
2623         de->d_ino = pc.get_ino_by_handle (get_handle ());
2624       de->d_type = DT_DIR;
2625       dir->__d_position++;
2626       dir->__flags |= dirent_saw_dot_dot;
2627       res = 0;
2628     }
2629
2630   syscall_printf ("%d = readdir(%p, %p) (L\"%lS\" > \"%ls\") (attr %y > type %d)",
2631                   res, dir, &de, res ? NULL : &fname, res ? "***" : de->d_name,
2632                   FileAttributes, de->d_type);
2633   return res;
2634 }
2635
2636 long
2637 fhandler_disk_file::telldir (DIR *dir)
2638 {
2639   return dir->__d_position;
2640 }
2641
2642 void
2643 fhandler_disk_file::seekdir (DIR *dir, long loc)
2644 {
2645   rewinddir (dir);
2646   while (loc > dir->__d_position)
2647     if (!::readdir (dir))
2648       break;
2649 }
2650
2651 void
2652 fhandler_disk_file::rewinddir (DIR *dir)
2653 {
2654   d_cachepos (dir) = 0;
2655   dir->__d_position = 0;
2656   d_mounts (dir)->rewind ();
2657 }
2658
2659 int
2660 fhandler_disk_file::closedir (DIR *dir)
2661 {
2662   int res = 0;
2663
2664   delete d_mounts (dir);
2665   syscall_printf ("%d = closedir(%p, %s)", res, dir, get_name ());
2666   return res;
2667 }
2668
2669 uint64_t
2670 fhandler_disk_file::fs_ioc_getflags ()
2671 {
2672   NTSTATUS status;
2673   IO_STATUS_BLOCK io;
2674   FILE_BASIC_INFORMATION fbi;
2675   FILE_CASE_SENSITIVE_INFORMATION fcsi;
2676   uint64_t flags = 0;
2677
2678   status = NtQueryInformationFile (get_handle (), &io, &fbi, sizeof fbi,
2679                                    FileBasicInformation);
2680   if (NT_SUCCESS (status))
2681     {
2682       flags = (uint64_t) fbi.FileAttributes & FS_FL_USER_VISIBLE;
2683       pc.file_attributes (fbi.FileAttributes);
2684     }
2685   else
2686     flags = (uint64_t) pc.file_attributes () & FS_FL_USER_VISIBLE;
2687   if (pc.isdir () && wincap.has_case_sensitive_dirs ()
2688       && !pc.isremote () && pc.fs_is_ntfs ())
2689     {
2690       fcsi.Flags = 0;
2691       status = NtQueryInformationFile (get_handle (), &io,
2692                                        &fcsi, sizeof fcsi,
2693                                        FileCaseSensitiveInformation);
2694       if (NT_SUCCESS (status)
2695           && (fcsi.Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR))
2696         flags |= FS_CASESENS_FL;
2697     }
2698   return flags;
2699 }
2700
2701 /* Settable DOS attributes */
2702 #define FS_FL_SETATTRIBS        (FS_READONLY_FL \
2703                                  | FS_HIDDEN_FL \
2704                                  | FS_SYSTEM_FL \
2705                                  | FS_ARCHIVE_FL \
2706                                  | FS_TEMP_FL \
2707                                  | FS_NOTINDEXED_FL\
2708                                  | FS_PINNED_FL \
2709                                  | FS_UNPINNED_FL)
2710
2711 int
2712 fhandler_disk_file::fs_ioc_setflags (uint64_t flags)
2713 {
2714   int ret = -1;
2715   uint64_t old_flags;
2716   HANDLE fh;
2717   NTSTATUS status;
2718   OBJECT_ATTRIBUTES attr;
2719   IO_STATUS_BLOCK io;
2720   FILE_BASIC_INFORMATION fbi;
2721   FILE_SET_SPARSE_BUFFER fssb;
2722   USHORT comp;
2723   FILE_CASE_SENSITIVE_INFORMATION fcsi;
2724
2725   if ((get_access () & (GENERIC_WRITE | FILE_WRITE_ATTRIBUTES)) != 0)
2726     fh = get_handle ();
2727   else
2728     {
2729       status = NtOpenFile (&fh, FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
2730                            pc.init_reopen_attr (attr, get_handle ()), &io,
2731                            FILE_SHARE_VALID_FLAGS,
2732                            FILE_OPEN_FOR_BACKUP_INTENT);
2733       if (!NT_SUCCESS (status))
2734         {
2735           fh = get_handle ();
2736           __seterrno_from_nt_status (status);
2737           goto out;
2738         }
2739     }
2740   old_flags = fs_ioc_getflags ();
2741   if ((old_flags & FS_FL_SETATTRIBS) != (flags & FS_FL_SETATTRIBS))
2742     {
2743       fbi.CreationTime.QuadPart
2744       = fbi.LastAccessTime.QuadPart
2745       = fbi.LastWriteTime.QuadPart
2746       = fbi.ChangeTime.QuadPart = 0LL;
2747       fbi.FileAttributes = (ULONG) old_flags;
2748       fbi.FileAttributes &= ~FS_FL_SETATTRIBS;
2749       fbi.FileAttributes |= (flags & FS_FL_SETATTRIBS);
2750       if (fbi.FileAttributes == 0)
2751         fbi.FileAttributes = FILE_ATTRIBUTE_NORMAL;
2752       status = NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
2753                                      FileBasicInformation);
2754       if (!NT_SUCCESS (status))
2755         {
2756           __seterrno_from_nt_status (status);
2757           goto out;
2758         }
2759     }
2760   if (!pc.isdir() && (flags & FS_SPARSE_FL) != (old_flags & FS_SPARSE_FL))
2761     {
2762       fssb.SetSparse = (flags & FS_SPARSE_FL) ? TRUE : FALSE;
2763       status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
2764                                 FSCTL_SET_SPARSE, &fssb, sizeof fssb, NULL, 0);
2765       if (!NT_SUCCESS (status))
2766         {
2767           __seterrno_from_nt_status (status);
2768           goto out;
2769         }
2770     }
2771   if (pc.isdir () && (flags & FS_CASESENS_FL) != (old_flags & FS_CASESENS_FL))
2772     {
2773       if (wincap.has_case_sensitive_dirs ()
2774           && !pc.isremote () && pc.fs_is_ntfs ())
2775         {
2776           fcsi.Flags = (flags & FS_CASESENS_FL)
2777                        ? FILE_CS_FLAG_CASE_SENSITIVE_DIR : 0;
2778           status = NtSetInformationFile (fh, &io, &fcsi, sizeof fcsi,
2779                                          FileCaseSensitiveInformation);
2780           if (!NT_SUCCESS (status))
2781             {
2782               __seterrno_from_nt_status (status);
2783               goto out;
2784             }
2785         }
2786       else
2787         {
2788           set_errno (ENOTSUP);
2789           goto out;
2790         }
2791     }
2792   if ((flags & FS_COMPR_FL) != (old_flags & FS_COMPR_FL))
2793     {
2794       if (fh != get_handle ())
2795         NtClose (fh);
2796       fh = NULL;
2797       if ((get_access () & (GENERIC_WRITE | GENERIC_READ))
2798           != (GENERIC_WRITE | GENERIC_READ))
2799         {
2800           status = NtOpenFile (&fh, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
2801                                pc.init_reopen_attr (attr, get_handle ()), &io,
2802                                FILE_SHARE_VALID_FLAGS,
2803                                FILE_SYNCHRONOUS_IO_NONALERT
2804                                | FILE_OPEN_FOR_BACKUP_INTENT);
2805           if (!NT_SUCCESS (status))
2806             {
2807               fh = get_handle ();
2808               __seterrno_from_nt_status (status);
2809               goto out;
2810             }
2811         }
2812       comp = (flags & FS_COMPR_FL)
2813              ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE;
2814       status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
2815                                 FSCTL_SET_COMPRESSION, &comp, sizeof comp,
2816                                 NULL, 0);
2817       if (!NT_SUCCESS (status))
2818         {
2819           __seterrno_from_nt_status (status);
2820           goto out;
2821         }
2822     }
2823   if (!pc.isdir() && (flags & FS_ENCRYPT_FL) != (old_flags & FS_ENCRYPT_FL))
2824     {
2825       tmp_pathbuf tp;
2826       PWCHAR path = tp.w_get ();
2827       BOOL cret;
2828
2829       /* EncryptFileW/DecryptFileW needs exclusive access. */
2830       if (fh != get_handle ())
2831         NtClose (fh);
2832       NtClose (get_handle ());
2833       set_handle (NULL);
2834
2835       pc.get_wide_win32_path (path);
2836       cret = (flags & FS_ENCRYPT_FL)
2837              ? EncryptFileW (path) : DecryptFileW (path, 0);
2838       status = NtOpenFile (&fh, get_access (),
2839                            pc.get_object_attr (attr, sec_none_nih), &io,
2840                            FILE_SHARE_VALID_FLAGS,
2841                            FILE_SYNCHRONOUS_IO_NONALERT
2842                            | FILE_OPEN_FOR_BACKUP_INTENT);
2843       if (!NT_SUCCESS (status))
2844         {
2845           __seterrno_from_nt_status (status);
2846           return -1;
2847         }
2848       set_handle (fh);
2849       if (!cret)
2850         {
2851           __seterrno ();
2852           goto out;
2853         }
2854     }
2855   ret = 0;
2856 out:
2857   status = NtQueryInformationFile (fh, &io, &fbi, sizeof fbi,
2858                                    FileBasicInformation);
2859   if (NT_SUCCESS (status))
2860     pc.file_attributes (fbi.FileAttributes);
2861   if (fh != get_handle ())
2862     NtClose (fh);
2863   return ret;
2864 }
2865
2866 int
2867 fhandler_disk_file::ioctl (unsigned int cmd, void *p)
2868 {
2869   int ret = -1;
2870   uint64_t flags = 0;
2871
2872   switch (cmd)
2873     {
2874     case FS_IOC_GETFLAGS:
2875       __try
2876         {
2877           uint64_t *fp = (uint64_t *) p;
2878           *fp = fs_ioc_getflags ();
2879           ret = 0;
2880         }
2881       __except (EFAULT) {}
2882       __endtry
2883       break;
2884     case FS_IOC_SETFLAGS:
2885       __try
2886         {
2887           flags = *(__uint64_t *) p;
2888         }
2889       __except (EFAULT)
2890         {
2891           break;
2892         }
2893       __endtry
2894       if (flags & ~FS_FL_USER_MODIFIABLE)
2895         {
2896           set_errno (EINVAL);
2897           break;
2898         }
2899       ret = fs_ioc_setflags (flags);
2900       break;
2901     default:
2902       ret = fhandler_base::ioctl (cmd, p);
2903       break;
2904     }
2905   syscall_printf ("%d = ioctl_file(%x, %p)", ret, cmd, p);
2906   return ret;
2907 }
This page took 0.244826 seconds and 5 git commands to generate.