1 /* fhandler_disk_file.cc
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
13 #include <cygwin/acl.h>
14 #include <sys/statvfs.h>
21 #include "shared_info.h"
29 #include <cygwin/fs.h>
34 enum __DIR_mount_type {
37 __DIR_mount_virt_target
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;
49 #define __DIR_PROC (MAX_MOUNTS)
50 #define __DIR_CYGDRIVE (MAX_MOUNTS+1)
51 #define __DIR_DEV (MAX_MOUNTS+2)
54 __DIR_mounts (const char *posix_path)
55 : parent_dir (posix_path)
57 parent_dir_len = strlen (parent_dir);
58 count = mount_table->get_mounts_here (parent_dir, parent_dir_len, mounts,
64 mount_table->free_mounts_here (mounts, count, &cygdrive);
66 /* For an entry within this dir, check if a mount point exists. */
67 bool check_mount (PUNICODE_STRING fname)
69 if (parent_dir_len == 1) /* root dir */
71 if (RtlEqualUnicodeString (fname, &ro_u_proc, FALSE))
73 found[__DIR_PROC] = true;
76 if (RtlEqualUnicodeString (fname, &ro_u_dev, FALSE))
78 found[__DIR_DEV] = true;
81 if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2
82 && RtlEqualUnicodeString (fname, &cygdrive, FALSE))
84 found[__DIR_CYGDRIVE] = true;
88 for (int i = 0; i < count; ++i)
89 if (RtlEqualUnicodeString (fname, &mounts[i], FALSE))
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)
100 for (int i = 0; i < count; ++i)
105 *retname = mounts[i];
106 return __DIR_mount_target;
108 if (parent_dir_len == 1) /* root dir */
110 if (!found[__DIR_PROC])
112 found[__DIR_PROC] = true;
114 *retname = ro_u_proc;
115 return __DIR_mount_virt_target;
117 if (!found[__DIR_DEV])
119 found[__DIR_DEV] = true;
122 return __DIR_mount_virt_target;
124 if (!found[__DIR_CYGDRIVE])
126 found[__DIR_CYGDRIVE] = true;
127 if (cygdrive.Length > 0)
131 return __DIR_mount_virt_target;
135 return __DIR_mount_none;
137 void rewind () { memset (found, 0, sizeof found); }
141 path_conv::isgood_inode (ino_t ino) const
143 /* If the FS doesn't support nonambiguous inode numbers anyway, bail out
145 if (!hasgood_inode ())
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 ())
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
154 if (fs_is_samba () && fs.samba_version () < 0x03050400)
156 /* Otherwise, trust the inode numbers unless proved otherwise. */
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. */
164 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr, bool remote)
170 UNICODE_STRING symbuf;
173 status = NtOpenFile (&reph, READ_CONTROL, attr, &io, FILE_SHARE_VALID_FLAGS,
175 | FILE_OPEN_FOR_BACKUP_INTENT
176 | FILE_OPEN_REPARSE_POINT);
177 if (NT_SUCCESS (status))
179 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER) tp.c_get ();
180 ret = (check_reparse_point_target (reph, remote, rp, &symbuf) > 0);
187 path_conv::get_ino_by_handle (HANDLE hdl)
190 FILE_INTERNAL_INFORMATION fii;
192 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fii, sizeof fii,
193 FileInternalInformation))
194 && isgood_inode (fii.IndexNumber.QuadPart))
195 return fii.IndexNumber.QuadPart;
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. */
204 fhandler_base::fstat_by_nfs_ea (struct stat *buf)
206 fattr3 *nfs_attr = pc.nfsattr ();
209 bool ldap_open = false;
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)
219 if (get_access () & GENERIC_WRITE)
220 FlushFileBuffers (get_handle ());
221 pc.get_finfo (get_handle ());
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 ())
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)
237 uid_t map_uid = ILLEGAL_UID;
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;
248 else /* fake files being owned by current user. */
249 buf->st_uid = myself->uid;
250 if (cygheap->pg.nss_grp_db ())
253 buf->st_gid = cygheap->ugid_cache.get_gid (nfs_attr->gid);
254 if (buf->st_gid == ILLEGAL_GID)
256 gid_t map_gid = ILLEGAL_GID;
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;
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;
284 fhandler_base::fstat_by_handle (struct stat *buf)
286 HANDLE h = get_stat_handle ();
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. */
293 status = pc.get_finfo (h);
294 if (!NT_SUCCESS (status))
296 debug_printf ("%y = NtQueryInformationFile(%S, FileAllInformation)",
297 status, pc.get_nt_native_path ());
301 if (pc.isgood_inode (pc.fai ()->InternalInformation.IndexNumber.QuadPart))
302 ino = pc.fai ()->InternalInformation.IndexNumber.QuadPart;
303 return fstat_helper (buf);
307 fhandler_base::fstat_by_name (struct stat *buf)
310 OBJECT_ATTRIBUTES attr;
312 UNICODE_STRING dirname;
313 UNICODE_STRING basename;
316 FILE_ID_BOTH_DIR_INFORMATION fdi;
317 WCHAR buf[NAME_MAX + 1];
320 if (!ino && pc.hasgood_inode () && !pc.has_buggy_fileid_dirinfo ())
322 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
323 InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
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);
334 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
335 &fdi_buf.fdi, sizeof fdi_buf,
336 FileIdBothDirectoryInformation,
337 TRUE, &basename, TRUE);
339 if (!NT_SUCCESS (status))
340 debug_printf ("%y = NtQueryDirectoryFile(%S)", status, &dirname);
342 ino = fdi_buf.fdi.FileId.QuadPart;
345 return fstat_helper (buf);
349 fhandler_base::fstat_fs (struct stat *buf)
353 int open_flags = O_RDONLY | O_BINARY;
355 if (get_stat_handle ())
360 res = fstat_by_nfs_ea (buf);
361 else if (!is_fs_special () || get_flags () & O_PATH)
362 res = fstat_by_handle (buf);
365 res = fstat_by_name (buf);
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);
375 query_open (query_read_attributes);
376 oret = open_fs (open_flags, 0);
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 ();
388 nohandle (no_handle);
392 res = fstat_by_name (buf);
398 fhandler_base::fstat_helper (struct stat *buf)
401 FILE_COMPRESSION_INFORMATION fci;
402 HANDLE h = get_stat_handle ();
403 PFILE_ALL_INFORMATION pfai = pc.fai ();
404 ULONG attributes = pc.file_attributes ();
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,
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 ()
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;
431 /* Enforce namehash as inode number on untrusted file systems. */
432 buf->st_ino = ino ?: get_ino ();
434 buf->st_blksize = PREFERRED_IO_BLKSIZE;
436 if (buf->st_size == 0
437 && pfai->StandardInformation.AllocationSize.QuadPart == 0LL)
438 /* File is empty and no blocks are preallocated. */
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)
458 /* Otherwise compute no. of blocks from file size. */
459 buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
462 /* Using a side effect: get_file_attributes checks for directory.
463 This is used, to set S_ISVTX, if needed. */
465 buf->st_mode = S_IFDIR;
466 else if (pc.issymlink ())
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;
475 else if (pc.issocket ())
476 buf->st_mode = S_IFSOCK;
478 if (!get_file_attribute (h, pc, buf->st_mode, &buf->st_uid, &buf->st_gid))
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);
485 if (buf->st_mode & S_IFMT)
487 else if (!is_fs_special ())
488 buf->st_mode |= S_IFREG;
491 buf->st_dev = buf->st_rdev = dev ();
492 buf->st_mode = dev ().mode ();
498 buf->st_mode |= STD_RBITS;
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 */
505 buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
506 else if (buf->st_mode & S_IFMT)
508 else if (is_fs_special ())
510 buf->st_dev = buf->st_rdev = dev ();
511 buf->st_mode = dev ().mode ();
516 buf->st_mode |= S_IFREG;
517 /* Check suffix for executable file. */
518 if (pc.exec_state () != is_executable)
520 PUNICODE_STRING path = pc.get_nt_native_path ();
522 if (RtlEqualUnicodePathSuffix (path, &ro_u_exe, TRUE)
523 || RtlEqualUnicodePathSuffix (path, &ro_u_lnk, TRUE))
526 /* No known suffix, check file header. This catches binaries and
528 if (pc.exec_state () == dont_know_if_executable)
530 OBJECT_ATTRIBUTES attr;
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
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 ());
547 LARGE_INTEGER off = { QuadPart:0LL };
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))
557 /* Heureka, it's an executable */
559 buf->st_mode |= STD_XBITS;
565 if (pc.exec_state () == is_executable)
566 buf->st_mode |= STD_XBITS;
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". */
575 buf->st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH);
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);
592 fhandler_disk_file::fstat (struct stat *buf)
594 return fstat_fs (buf);
598 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
600 int ret = -1, opened = 0;
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
606 HANDLE fh = get_handle ();
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,
615 | FILE_OPEN_FOR_BACKUP_INTENT));
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,
625 | FILE_OPEN_FOR_BACKUP_INTENT));
631 ret = fstatvfs_by_handle (fh, sfs);
635 syscall_printf ("%d = fstatvfs(%s, %p)", ret, get_name (), sfs);
640 fhandler_base::fstatvfs_by_handle (HANDLE fh, struct statvfs *sfs)
645 FILE_FS_FULL_SIZE_INFORMATION full_fsi;
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))
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)
667 /* Quotas active. We can't trust TotalAllocationUnits. */
668 NTFS_VOLUME_DATA_BUFFER nvdb;
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 ());
677 sfs->f_blocks = (fsblkcnt_t) nvdb.TotalClusters.QuadPart;
681 else if (status == STATUS_INVALID_PARAMETER /* Netapp */
682 || status == STATUS_INVALID_INFO_CLASS)
684 FILE_FS_SIZE_INFORMATION fsi;
685 status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
686 FileFsSizeInformation);
687 if (NT_SUCCESS (status))
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;
697 debug_printf ("%y = NtQueryVolumeInformationFile"
698 "(%S, FileFsSizeInformation)",
699 status, pc.get_nt_native_path ());
702 debug_printf ("%y = NtQueryVolumeInformationFile"
703 "(%S, FileFsFullSizeInformation)",
704 status, pc.get_nt_native_path ());
709 fhandler_disk_file::fchmod (mode_t mode)
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);
724 query_open (query_write_dac);
725 if (!(oret = open (O_BINARY, 0)))
727 /* Need WRITE_DAC to write ACLs. */
730 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
731 query_open (query_write_attributes);
732 if (!(oret = open (O_BINARY, 0)))
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. */
743 FILE_FULL_EA_INFORMATION ffei;
744 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
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);
767 security_descriptor sd, sd_ret;
772 bool standard_acl = false;
774 mode_t attr = pc.isdir () ? S_IFDIR : 0;
776 if (!get_file_sd (get_handle (), pc, sd, false))
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)
783 /* Overwrite ACL permissions as required by POSIX 1003.1e
785 aclp[0].a_perm = (mode >> 6) & S_IRWXO;
787 /* POSIXly correct: If CLASS_OBJ is present, chmod only modifies
788 CLASS_OBJ, not GROUP_OBJ.
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. */
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;
802 if ((idx = searchace (aclp, nentries, OTHER_OBJ)) >= 0)
803 aclp[idx].a_perm = mode & S_IRWXO;
806 if (set_posix_access (mode, uid, gid, aclp, nentries, sd_ret,
808 ret = set_file_sd (get_handle (), pc, sd_ret, false);
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;
819 pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
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)
829 OBJECT_ATTRIBUTES attr;
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)))
837 NtSetAttributesFile (fh, pc.file_attributes ());
841 /* Correct NTFS security attributes have higher priority */
844 if (!NT_SUCCESS (status))
845 __seterrno_from_nt_status (status);
858 fhandler_disk_file::fchown (uid_t uid, gid_t gid)
862 security_descriptor sd, sd_ret;
863 mode_t attr = pc.isdir () ? S_IFDIR : 0;
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. */
880 query_open (query_write_control);
881 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
885 if (get_file_sd (get_handle (), pc, sd, false))
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)
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)
901 if (uid == ILLEGAL_UID)
903 else if (gid == ILLEGAL_GID)
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. */
913 for (int idx = 0; idx < nentries; ++idx)
915 aclp[idx].a_perm |= S_IROTH;
916 if (aclp[idx].a_type & USER_OBJ)
917 aclp[idx].a_perm |= S_IWOTH;
920 if (set_posix_access (attr, uid, gid, aclp, nentries, sd_ret,
922 ret = set_file_sd (get_handle (), pc, sd_ret, true);
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 ())
939 || ((sid = sidfromuid (old_uid, NULL)) != NO_SID
940 && RtlEqualPrefixSid (sid,
941 well_known_samba_unix_user_fake_sid)))
943 debug_printf ("Faking chown worked on standalone Samba");
956 fhandler_disk_file::facl (int cmd, int nentries, aclent_t *aclbufp)
968 /* Open for writing required to be able to set ctime
969 (even though setting the ACL is just pretended). */
971 oret = open (O_WRONLY | O_BINARY, 0);
977 else if (nentries < MIN_ACL_ENTRIES)
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;
998 res = MIN_ACL_ENTRIES;
1007 if ((cmd == SETACL && !get_handle ())
1008 || (cmd != SETACL && !get_stat_handle ()))
1010 query_open (cmd == SETACL ? query_write_dac : query_read_control);
1011 if (!(oret = open (O_BINARY, 0)))
1013 if (cmd == GETACL || cmd == GETACLCNT)
1014 goto cant_access_acl;
1021 if (!aclsort (nentries, 0, aclbufp))
1024 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
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);
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;
1052 res = getacl (get_stat_handle (), pc, 0, NULL);
1054 if (res == -1 && get_errno () == ENOSYS)
1055 goto cant_access_acl;
1070 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1072 if (pc.is_fs_special ())
1074 set_errno (ENOTSUP);
1077 return read_ea (get_handle (), pc, name, (char *) value, size);
1081 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1084 if (pc.is_fs_special ())
1086 set_errno (ENOTSUP);
1089 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1093 fhandler_disk_file::fadvise (off_t offset, off_t length, int advice)
1095 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
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. */
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;
1111 FILE_MODE_INFORMATION fmi;
1112 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1114 FileModeInformation);
1115 if (NT_SUCCESS (status))
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))
1124 __seterrno_from_nt_status (status);
1127 return geterrno_from_nt_status (status);
1131 fhandler_disk_file::falloc_allocate (int mode, off_t offset, off_t length)
1135 FILE_STANDARD_INFORMATION fsi;
1136 FILE_END_OF_FILE_INFORMATION feofi;
1137 FILE_ALLOCATION_INFORMATION fai = { 0 };
1140 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1141 FileStandardInformation);
1142 if (!NT_SUCCESS (status))
1143 return geterrno_from_nt_status (status);
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)
1157 if (!has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1159 feofi.EndOfFile.QuadPart = fsi.EndOfFile.QuadPart;
1162 feofi.EndOfFile.QuadPart = offset + length;
1164 case __FALLOC_FL_TRUNCATE:
1165 /* For ftruncate(2), offset is 0. Just use length as is. */
1166 feofi.EndOfFile.QuadPart = length;
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))
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 ());
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;
1195 feofi.EndOfFile.QuadPart = fsi.EndOfFile.QuadPart;
1199 /* Now set the new EOF */
1200 if (feofi.EndOfFile.QuadPart != fsi.EndOfFile.QuadPart)
1202 status = NtSetInformationFile (get_handle (), &io,
1203 &feofi, sizeof feofi,
1204 FileEndOfFileInformation);
1205 if (!NT_SUCCESS (status))
1206 return geterrno_from_nt_status (status);
1209 /* If called via fallocate(2) or posix_fallocate(3), allocate blocks in
1210 sparse file holes. */
1211 if (mode != __FALLOC_FL_TRUNCATE
1213 && has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
1215 int res = falloc_zero_range (mode | __FALLOC_FL_ZERO_HOLES,
1221 /* Last but not least, set the new allocation size, if any */
1222 if (fai.AllocationSize.QuadPart)
1224 /* This is not fatal. Just note a failure in the debug output. */
1225 status = NtSetInformationFile (get_handle (), &io,
1227 FileAllocationInformation);
1228 if (!NT_SUCCESS (status))
1229 debug_printf ("%y = NtSetInformationFile(%S, "
1230 "FileAllocationInformation)",
1231 status, pc.get_nt_native_path ());
1238 fhandler_disk_file::falloc_punch_hole (off_t offset, off_t length)
1242 FILE_STANDARD_INFORMATION fsi;
1243 FILE_ZERO_DATA_INFORMATION fzi;
1246 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1247 FileStandardInformation);
1248 if (!NT_SUCCESS (status))
1249 return geterrno_from_nt_status (status);
1251 if (offset > fsi.EndOfFile.QuadPart) /* no-op */
1254 if (offset + length > fsi.EndOfFile.QuadPart)
1255 length = fsi.EndOfFile.QuadPart - offset;
1257 /* If the file isn't sparse yet, make it so. */
1258 if (!has_attribute (FILE_ATTRIBUTE_SPARSE_FILE))
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);
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);
1282 fhandler_disk_file::falloc_zero_range (int mode, off_t offset, off_t length)
1286 FILE_STANDARD_INFORMATION fsi;
1287 FILE_ALLOCATED_RANGE_BUFFER inp, *out = NULL;
1288 OBJECT_ATTRIBUTES attr;
1291 size_t data_chunk_count = 0;
1294 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1295 FileStandardInformation);
1296 if (!NT_SUCCESS (status))
1297 return geterrno_from_nt_status (status);
1299 /* offset and length must not exceed EOF with FALLOC_FL_KEEP_SIZE */
1300 if (mode & FALLOC_FL_KEEP_SIZE)
1302 if (offset > fsi.EndOfFile.QuadPart) /* no-op */
1305 if (offset + length > fsi.EndOfFile.QuadPart)
1306 length = fsi.EndOfFile.QuadPart - offset;
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))
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;
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);
1331 /* FILE_SPARSE_GRANULARITY == 2 * NT_MAX_PATH ==> fits exactly */
1332 char *nullbuf = tp.t_get ();
1333 memset (nullbuf, 0, FILE_SPARSE_GRANULARITY);
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;
1343 bool in_data = true;
1345 if (off.QuadPart % FILE_SPARSE_GRANULARITY) /* First block */
1346 chunk_len = roundup2 (off.QuadPart, FILE_SPARSE_GRANULARITY) - off.QuadPart;
1348 chunk_len = FILE_SPARSE_GRANULARITY;
1349 if (chunk_len > length) /* First or last block */
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)
1357 for (size_t idx = start_idx; idx < data_chunk_count; ++idx)
1358 if (off.QuadPart >= out[idx].FileOffset.QuadPart)
1360 /* Skip entries with lower start address next time. */
1362 if (off.QuadPart < out[idx].FileOffset.QuadPart
1363 + out[idx].Length.QuadPart)
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)
1376 status = NtWriteFile (zo_handle, NULL, NULL, NULL, &io, nullbuf,
1377 in_data ? chunk_len : 1, &off, NULL);
1378 if (!NT_SUCCESS (status))
1380 res = geterrno_from_nt_status (status);
1385 off.QuadPart += chunk_len;
1386 length -= chunk_len;
1389 NtClose (zo_handle);
1394 fhandler_disk_file::fallocate (int mode, off_t offset, off_t length)
1396 if (length < 0 || !get_handle ())
1400 if (!(get_access () & GENERIC_WRITE))
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))
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);
1424 fhandler_disk_file::link (const char *newpath)
1426 size_t nlen = strlen (newpath);
1427 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
1430 set_errno (newpc.error);
1434 if (newpc.exists ())
1436 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1441 if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
1447 char new_buf[nlen + 5];
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
1453 if (pc.is_lnk_special ()
1454 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1457 /* Shortcut hack. */
1458 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1460 newpc.check (newpath, PC_SYM_NOFOLLOW);
1462 else if (!pc.isdir ()
1464 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1466 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1469 /* Executable hack. */
1470 stpcpy (stpcpy (new_buf, newpath), ".exe");
1472 newpc.check (newpath, PC_SYM_NOFOLLOW);
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 ();
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);
1494 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1495 if (!NT_SUCCESS (status))
1497 if (status == STATUS_INVALID_DEVICE_REQUEST
1498 || status == STATUS_NOT_SUPPORTED)
1499 /* FS doesn't support hard links. Linux returns EPERM. */
1502 __seterrno_from_nt_status (status);
1505 else if ((pc.file_attributes () & O_TMPFILE_FILE_ATTRS)
1506 == O_TMPFILE_FILE_ATTRS)
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
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);
1532 FILE_BASIC_INFORMATION fbi;
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",
1550 fhandler_disk_file::utimens (const struct timespec *tvp)
1552 return utimens_fs (tvp);
1556 fhandler_base::utimens_fs (const struct timespec *tvp)
1558 struct timespec timeofday;
1559 struct timespec tmp[2];
1560 bool closeit = false;
1564 query_open (query_write_attributes);
1565 if (!open_fs (O_BINARY, 0))
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))
1574 syscall_printf ("Opening file failed");
1581 clock_gettime (CLOCK_REALTIME, &timeofday);
1583 tmp[1] = tmp[0] = timeofday;
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))
1594 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1595 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1597 debug_printf ("incoming lastaccess %ly %ly", tmp[0].tv_sec, tmp[0].tv_nsec);
1600 FILE_BASIC_INFORMATION fbi;
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)
1614 OBJECT_ATTRIBUTES attr;
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)))
1622 NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1623 FileBasicInformation);
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)
1633 __seterrno_from_nt_status (status);
1639 fhandler_disk_file::fhandler_disk_file () :
1640 fhandler_base (), prw_handle (NULL)
1644 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1645 fhandler_base (), prw_handle (NULL)
1651 fhandler_disk_file::open (int flags, mode_t mode)
1653 return open_fs (flags, mode);
1657 fhandler_disk_file::close (int flag)
1659 /* Close extra pread/pwrite handle, if it exists. */
1661 NtClose (prw_handle);
1662 return fhandler_base::close ();
1666 fhandler_disk_file::fcntl (int cmd, intptr_t arg)
1672 case F_LCK_MANDATORY: /* Mandatory locking only works on files. */
1673 mandatory_locking (!!arg);
1674 need_fork_fixup (true);
1678 res = fhandler_base::fcntl (cmd, arg);
1685 fhandler_disk_file::dup (fhandler_base *child, int flags)
1687 fhandler_disk_file *fhc = (fhandler_disk_file *) child;
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;
1699 fhandler_disk_file::fixup_after_fork (HANDLE parent)
1702 mandatory_locking (false);
1703 fhandler_base::fixup_after_fork (parent);
1707 fhandler_base::open_fs (int flags, mode_t mode)
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)
1717 bool new_file = !exists ();
1719 int res = fhandler_base::open (flags, mode);
1722 /* The file info in pc is wrong at this point for newly created files.
1723 Refresh it before fetching any file info. */
1725 pc.get_finfo (get_handle ());
1727 if (pc.isgood_inode (pc.get_ino ()))
1728 ino = pc.get_ino ();
1731 syscall_printf ("%d = fhandler_disk_file::open(%S, %y)", res,
1732 pc.get_nt_native_path (), flags);
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.
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
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
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. */
1773 fhandler_disk_file::prw_open (bool write, void *aio)
1777 OBJECT_ATTRIBUTES attr;
1778 ULONG options = get_options ();
1780 /* If async i/o is intended, turn off the default synchronous operation */
1782 options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
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)
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);
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))
1802 __seterrno_from_nt_status (status);
1806 /* prw_handle is invalid after fork. */
1807 need_fork_fixup (true);
1809 /* record prw_handle's asyncness for subsequent pread/pwrite operations */
1810 prw_handle_isasync = !!aio;
1815 fhandler_disk_file::pread (void *buf, size_t count, off_t offset, void *aio)
1817 struct aiocb *aiocb = (struct aiocb *) aio;
1820 if ((get_flags () & O_ACCMODE) == O_WRONLY)
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 ())
1830 extern int is_at_eof (HANDLE h);
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;
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;
1841 if (!prw_handle && prw_open (false, aio))
1843 status = NtReadFile (prw_handle, evt, NULL, NULL, pio, buf, count,
1845 if (status == STATUS_END_OF_FILE)
1847 else if (!NT_SUCCESS (status))
1854 if (status == (NTSTATUS) STATUS_ACCESS_VIOLATION)
1856 if (is_at_eof (prw_handle))
1861 switch (mmap_is_attached_or_noreserve (buf, count))
1863 case MMAP_NORESERVE_COMMITED:
1864 status = NtReadFile (prw_handle, evt, NULL, NULL, pio,
1865 buf, count, &off, NULL);
1866 if (NT_SUCCESS (status))
1868 res = aio ? (ssize_t) aiocb->aio_wincb.info
1873 case MMAP_RAISE_SIGBUS:
1879 __seterrno_from_nt_status (status);
1884 res = aio ? (ssize_t) aiocb->aio_wincb.info : io.Information;
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)
1897 size_t tmp_count = count;
1898 read (buf, tmp_count);
1899 if (lseek (curpos, SEEK_SET) >= 0)
1900 res = (ssize_t) tmp_count;
1905 /* If this was a disallowed async request, simulate its conclusion */
1908 aiocb->aio_rbytes = res;
1909 aiocb->aio_errno = res == -1 ? get_errno () : 0;
1910 SetEvent ((HANDLE) aiocb->aio_wincb.event);
1914 debug_printf ("%d = pread(%p, %ld, %D, %p)\n", res, buf, count, offset, aio);
1919 fhandler_disk_file::pwrite (void *buf, size_t count, off_t offset, void *aio)
1921 struct aiocb *aiocb = (struct aiocb *) aio;
1924 if ((get_flags () & O_ACCMODE) == O_RDONLY)
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 ())
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;
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;
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))
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 ());
1964 if (!prw_handle && prw_open (true, aio))
1966 status = NtWriteFile (prw_handle, evt, NULL, NULL, pio, buf, count,
1968 if (!NT_SUCCESS (status))
1970 __seterrno_from_nt_status (status);
1973 res = aio ? (ssize_t) aiocb->aio_wincb.info : io.Information;
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)
1985 res = (ssize_t) write (buf, count);
1986 if (lseek (curpos, SEEK_SET) < 0)
1990 /* If this was a disallowed async request, simulate its conclusion */
1993 aiocb->aio_rbytes = res;
1994 aiocb->aio_errno = res == -1 ? get_errno () : 0;
1995 SetEvent ((HANDLE) aiocb->aio_wincb.event);
1999 debug_printf ("%d = pwrite(%p, %ld, %D, %p)\n", res, buf, count, offset, aio);
2004 fhandler_disk_file::mkdir (mode_t mode)
2007 SECURITY_ATTRIBUTES sa = sec_none_nih;
2010 OBJECT_ATTRIBUTES attr;
2012 PFILE_FULL_EA_INFORMATION p = NULL;
2014 ULONG access = FILE_LIST_DIRECTORY | SYNCHRONIZE;
2016 if (pc.fs_is_nfs ())
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)
2023 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
2024 p->NextEntryOffset = 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;
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,
2051 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
2052 | FILE_OPEN_FOR_BACKUP_INTENT,
2054 if (NT_SUCCESS (status))
2056 /* Set the "directory attribute" so that pc.isdir() returns correct
2057 value in subsequent function calls. */
2058 pc.file_attributes (FILE_ATTRIBUTE_DIRECTORY);
2060 set_created_file_access (dir, pc, mode & 07777);
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. */
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 ())
2074 PUNICODE_STRING new_dir = pc.get_nt_native_path ();
2075 PUNICODE_STRING root_dir = &cygheap->installation_root;
2077 if (RtlEqualUnicodePathPrefix (new_dir, root_dir, TRUE)
2078 && new_dir->Buffer[root_dir->Length / sizeof (WCHAR)] == L'\\')
2080 FILE_CASE_SENSITIVE_INFORMATION fcsi;
2082 fcsi.Flags = FILE_CS_FLAG_CASE_SENSITIVE_DIR;
2083 status = NtSetInformationFile (dir, &io, &fcsi, sizeof fcsi,
2084 FileCaseSensitiveInformation);
2085 if (!NT_SUCCESS (status))
2087 debug_printf ("Setting dir case sensitivity, status %y",
2089 if (status == STATUS_NOT_SUPPORTED)
2091 debug_printf ("Dir case sensitivity requires WSL");
2092 wincap.disable_case_sensitive_dirs ();
2102 __seterrno_from_nt_status (status);
2108 fhandler_disk_file::rmdir ()
2112 set_errno (ENOTDIR);
2121 NTSTATUS status = unlink_nt (pc, false);
2123 if (!NT_SUCCESS (status))
2125 __seterrno_from_nt_status (status);
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. */
2136 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
2138 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
2139 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
2140 + (NAME_MAX + 1) * sizeof (WCHAR)))
2144 char __cache[DIR_BUF_SIZE];
2148 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
2149 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
2151 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
2154 fhandler_disk_file::opendir (int fd)
2159 if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
2161 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
2167 else if ((dir->__d_dirent =
2168 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
2176 if (cfd < 0 && fd < 0)
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;
2187 if (pc.iscygdrive ())
2189 if (fd < 0 && !open (O_RDONLY, 0))
2194 dir->__d_internal = (uintptr_t) new __DIR_mounts (get_name ());
2195 d_cachepos (dir) = 0;
2198 /* opendir() case. Initialize with given directory name and
2199 NULL directory handle. */
2200 OBJECT_ATTRIBUTES attr;
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
2214 ACCESS_MASK fstat_mask = READ_CONTROL | FILE_READ_ATTRIBUTES;
2218 status = NtOpenFile (&get_handle (),
2219 SYNCHRONIZE | FILE_LIST_DIRECTORY
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))
2228 if (status == STATUS_ACCESS_DENIED && fstat_mask)
2232 __seterrno_from_nt_status (status);
2237 while (!NT_SUCCESS (status));
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.
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 ())
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;
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... */
2274 set_close_on_exec (true);
2279 syscall_printf ("%p = opendir (%s)", res, get_name ());
2283 delete d_mounts (dir);
2285 free (dir->__d_dirent);
2287 free (dir->__d_dirname);
2294 readdir_get_ino (const char *path, bool dot_dot)
2299 OBJECT_ATTRIBUTES attr;
2305 fname = (char *) alloca (strlen (path) + 4);
2306 char *c = stpcpy (fname, path);
2312 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE);
2313 if (pc.isspecial ())
2315 if (!stat_worker (pc, &st))
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,
2325 | FILE_OPEN_FOR_BACKUP_INTENT
2326 | (pc.is_known_reparse_point ()
2327 ? FILE_OPEN_REPARSE_POINT : 0)))
2330 ino = pc.get_ino_by_handle (hdl);
2332 ino = hash_path_name (0, pc.get_nt_native_path ());
2338 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
2339 DWORD attr, PUNICODE_STRING fname)
2343 switch (d_mounts (dir)->check_missing_mount (fname))
2345 case __DIR_mount_none:
2347 return geterrno_from_win_error (w32_err);
2348 case __DIR_mount_virt_target:
2349 de->d_type = DT_DIR;
2355 dir->__flags &= ~dirent_set_d_ino;
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))
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;
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. *.
2376 if ((attr & FILE_ATTRIBUTE_REPARSE_POINT) && !isoffline (attr))
2378 OBJECT_ATTRIBUTES oattr;
2380 InitializeObjectAttributes (&oattr, fname, pc.objcaseinsensitive (),
2381 get_handle (), NULL);
2382 if (readdir_check_reparse_point (&oattr, isremote ()))
2383 de->d_type = DT_LNK;
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))
2393 UNICODE_STRING uname;
2395 RtlInitCountedUnicodeString (&uname,
2397 + fname->Length / sizeof (WCHAR) - 4,
2398 4 * sizeof (WCHAR));
2399 if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
2402 char *file = tp.c_get ();
2403 char *p = stpcpy (file, pc.get_posix ());
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 ())
2411 fname->Length -= 4 * sizeof (WCHAR);
2412 de->d_type = DT_LNK;
2414 else if (fpath.isfifo ())
2416 fname->Length -= 4 * sizeof (WCHAR);
2417 de->d_type = DT_FIFO;
2419 else if (fpath.is_fs_special ())
2421 fname->Length -= 4 * sizeof (WCHAR);
2422 de->d_type = S_ISCHR (fpath.dev.mode ()) ? DT_CHR : DT_BLK;
2427 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
2428 fname->Length / sizeof (WCHAR));
2430 /* Don't try to optimize relative to dir->__d_position. On several
2431 filesystems it's no safe bet that "." and ".." entries always
2433 if (de->d_name[0] == '.')
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;
2444 fhandler_disk_file::readdir (DIR *dir, dirent *de)
2447 NTSTATUS status = STATUS_SUCCESS;
2448 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
2450 ULONG FileNameLength;
2451 ULONG FileAttributes;
2453 UNICODE_STRING fname;
2455 /* d_cachepos always refers to the next cache entry to use. If it's 0
2456 we must reload the cache. */
2458 if (d_cachepos (dir) == 0)
2460 if ((dir->__flags & dirent_get_d_ino))
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;
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);
2488 if (status == STATUS_NO_MORE_FILES)
2490 else if (!NT_SUCCESS (status))
2491 debug_printf ("NtQueryDirectoryFile failed, status %y, win32 error %u",
2492 status, RtlNtStatusToDosError (status));
2495 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
2496 if (buf->NextEntryOffset == 0)
2497 d_cachepos (dir) = 0;
2499 d_cachepos (dir) += buf->NextEntryOffset;
2500 if ((dir->__flags & dirent_get_d_ino))
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;
2508 else if ((dir->__flags & dirent_nfs_d_ino))
2510 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
2511 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
2515 FileName = ((PFILE_BOTH_DIR_INFORMATION) buf)->FileName;
2517 ((PFILE_BOTH_DIR_INFORMATION) buf)->FileNameLength;
2519 ((PFILE_BOTH_DIR_INFORMATION) buf)->FileAttributes;
2521 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
2522 d_mounts (dir)->check_mount (&fname);
2523 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
2525 /* Don't try to optimize relative to dir->__d_position. On several
2526 filesystems it's no safe bet that "." and ".." entries always
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'.')
2533 if (!(dir->__flags & dirent_isroot))
2534 de->d_ino = readdir_get_ino (get_name (), true);
2536 de->d_ino = pc.get_ino_by_handle (get_handle ());
2540 OBJECT_ATTRIBUTES attr;
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.
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
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,
2570 | FILE_OPEN_FOR_BACKUP_INTENT
2571 | FILE_OPEN_REPARSE_POINT);
2572 if (NT_SUCCESS (f_status))
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))
2584 FILE_BASIC_INFORMATION fbi;
2586 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fbi,
2587 sizeof fbi, FileBasicInformation)))
2588 FileAttributes = fbi.FileAttributes;
2591 if (NT_SUCCESS (f_status))
2593 if (pc.isgood_inode (fii.IndexNumber.QuadPart))
2594 de->d_ino = fii.IndexNumber.QuadPart;
2596 /* Untrusted file system. Don't try to fetch inode
2598 dir->__flags &= ~dirent_set_d_ino;
2605 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
2606 FileAttributes, &fname)))
2607 dir->__d_position++;
2608 else if (!(dir->__flags & dirent_saw_dot))
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;
2617 else if (!(dir->__flags & dirent_saw_dot_dot))
2619 strcpy (de->d_name , "..");
2620 if (!(dir->__flags & dirent_isroot))
2621 de->d_ino = readdir_get_ino (get_name (), true);
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;
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);
2637 fhandler_disk_file::telldir (DIR *dir)
2639 return dir->__d_position;
2643 fhandler_disk_file::seekdir (DIR *dir, long loc)
2646 while (loc > dir->__d_position)
2647 if (!::readdir (dir))
2652 fhandler_disk_file::rewinddir (DIR *dir)
2654 d_cachepos (dir) = 0;
2655 dir->__d_position = 0;
2656 d_mounts (dir)->rewind ();
2660 fhandler_disk_file::closedir (DIR *dir)
2664 delete d_mounts (dir);
2665 syscall_printf ("%d = closedir(%p, %s)", res, dir, get_name ());
2670 fhandler_disk_file::fs_ioc_getflags ()
2674 FILE_BASIC_INFORMATION fbi;
2675 FILE_CASE_SENSITIVE_INFORMATION fcsi;
2678 status = NtQueryInformationFile (get_handle (), &io, &fbi, sizeof fbi,
2679 FileBasicInformation);
2680 if (NT_SUCCESS (status))
2682 flags = (uint64_t) fbi.FileAttributes & FS_FL_USER_VISIBLE;
2683 pc.file_attributes (fbi.FileAttributes);
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 ())
2691 status = NtQueryInformationFile (get_handle (), &io,
2693 FileCaseSensitiveInformation);
2694 if (NT_SUCCESS (status)
2695 && (fcsi.Flags & FILE_CS_FLAG_CASE_SENSITIVE_DIR))
2696 flags |= FS_CASESENS_FL;
2701 /* Settable DOS attributes */
2702 #define FS_FL_SETATTRIBS (FS_READONLY_FL \
2712 fhandler_disk_file::fs_ioc_setflags (uint64_t flags)
2718 OBJECT_ATTRIBUTES attr;
2720 FILE_BASIC_INFORMATION fbi;
2721 FILE_SET_SPARSE_BUFFER fssb;
2723 FILE_CASE_SENSITIVE_INFORMATION fcsi;
2725 if ((get_access () & (GENERIC_WRITE | FILE_WRITE_ATTRIBUTES)) != 0)
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))
2736 __seterrno_from_nt_status (status);
2740 old_flags = fs_ioc_getflags ();
2741 if ((old_flags & FS_FL_SETATTRIBS) != (flags & FS_FL_SETATTRIBS))
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))
2756 __seterrno_from_nt_status (status);
2760 if (!pc.isdir() && (flags & FS_SPARSE_FL) != (old_flags & FS_SPARSE_FL))
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))
2767 __seterrno_from_nt_status (status);
2771 if (pc.isdir () && (flags & FS_CASESENS_FL) != (old_flags & FS_CASESENS_FL))
2773 if (wincap.has_case_sensitive_dirs ()
2774 && !pc.isremote () && pc.fs_is_ntfs ())
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))
2782 __seterrno_from_nt_status (status);
2788 set_errno (ENOTSUP);
2792 if ((flags & FS_COMPR_FL) != (old_flags & FS_COMPR_FL))
2794 if (fh != get_handle ())
2797 if ((get_access () & (GENERIC_WRITE | GENERIC_READ))
2798 != (GENERIC_WRITE | GENERIC_READ))
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))
2808 __seterrno_from_nt_status (status);
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,
2817 if (!NT_SUCCESS (status))
2819 __seterrno_from_nt_status (status);
2823 if (!pc.isdir() && (flags & FS_ENCRYPT_FL) != (old_flags & FS_ENCRYPT_FL))
2826 PWCHAR path = tp.w_get ();
2829 /* EncryptFileW/DecryptFileW needs exclusive access. */
2830 if (fh != get_handle ())
2832 NtClose (get_handle ());
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))
2845 __seterrno_from_nt_status (status);
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 ())
2867 fhandler_disk_file::ioctl (unsigned int cmd, void *p)
2874 case FS_IOC_GETFLAGS:
2877 uint64_t *fp = (uint64_t *) p;
2878 *fp = fs_ioc_getflags ();
2881 __except (EFAULT) {}
2884 case FS_IOC_SETFLAGS:
2887 flags = *(__uint64_t *) p;
2894 if (flags & ~FS_FL_USER_MODIFIABLE)
2899 ret = fs_ioc_setflags (flags);
2902 ret = fhandler_base::ioctl (cmd, p);
2905 syscall_printf ("%d = ioctl_file(%x, %p)", ret, cmd, p);