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 ()
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 /* record prw_handle's asyncness for subsequent pread/pwrite operations */
1807 prw_handle_isasync
= !!aio
;
1812 fhandler_disk_file::pread (void *buf
, size_t count
, off_t offset
, void *aio
)
1814 struct aiocb
*aiocb
= (struct aiocb
*) aio
;
1817 if ((get_flags () & O_ACCMODE
) == O_WRONLY
)
1823 /* In binary mode, we can use an atomic NtReadFile call.
1824 Windows mandatory locking semantics disallow to use another HANDLE. */
1825 if (rbinary () && !mandatory_locking ())
1827 extern int is_at_eof (HANDLE h
);
1830 LARGE_INTEGER off
= { QuadPart
:offset
};
1831 HANDLE evt
= aio
? (HANDLE
) aiocb
->aio_wincb
.event
: NULL
;
1832 PIO_STATUS_BLOCK pio
= aio
? (PIO_STATUS_BLOCK
) &aiocb
->aio_wincb
: &io
;
1834 /* If existing prw_handle asyncness doesn't match this call's, re-open */
1835 if (prw_handle
&& (prw_handle_isasync
!= !!aio
))
1836 NtClose (prw_handle
), prw_handle
= NULL
;
1838 if (!prw_handle
&& prw_open (false, aio
))
1840 status
= NtReadFile (prw_handle
, evt
, NULL
, NULL
, pio
, buf
, count
,
1842 if (status
== STATUS_END_OF_FILE
)
1844 else if (!NT_SUCCESS (status
))
1851 if (status
== (NTSTATUS
) STATUS_ACCESS_VIOLATION
)
1853 if (is_at_eof (prw_handle
))
1858 switch (mmap_is_attached_or_noreserve (buf
, count
))
1860 case MMAP_NORESERVE_COMMITED
:
1861 status
= NtReadFile (prw_handle
, evt
, NULL
, NULL
, pio
,
1862 buf
, count
, &off
, NULL
);
1863 if (NT_SUCCESS (status
))
1865 res
= aio
? (ssize_t
) aiocb
->aio_wincb
.info
1870 case MMAP_RAISE_SIGBUS
:
1876 __seterrno_from_nt_status (status
);
1881 res
= aio
? (ssize_t
) aiocb
->aio_wincb
.info
: io
.Information
;
1888 /* Text mode stays slow and non-atomic. */
1889 off_t curpos
= lseek (0, SEEK_CUR
);
1890 if (curpos
< 0 || lseek (offset
, SEEK_SET
) < 0)
1894 size_t tmp_count
= count
;
1895 read (buf
, tmp_count
);
1896 if (lseek (curpos
, SEEK_SET
) >= 0)
1897 res
= (ssize_t
) tmp_count
;
1902 /* If this was a disallowed async request, simulate its conclusion */
1905 aiocb
->aio_rbytes
= res
;
1906 aiocb
->aio_errno
= res
== -1 ? get_errno () : 0;
1907 SetEvent ((HANDLE
) aiocb
->aio_wincb
.event
);
1911 debug_printf ("%d = pread(%p, %ld, %D, %p)\n", res
, buf
, count
, offset
, aio
);
1916 fhandler_disk_file::pwrite (void *buf
, size_t count
, off_t offset
, void *aio
)
1918 struct aiocb
*aiocb
= (struct aiocb
*) aio
;
1921 if ((get_flags () & O_ACCMODE
) == O_RDONLY
)
1927 /* In binary mode, we can use an atomic NtWriteFile call.
1928 Windows mandatory locking semantics disallow to use another HANDLE. */
1929 if (wbinary () && !mandatory_locking ())
1933 FILE_STANDARD_INFORMATION fsi
;
1934 LARGE_INTEGER off
= { QuadPart
:offset
};
1935 HANDLE evt
= aio
? (HANDLE
) aiocb
->aio_wincb
.event
: NULL
;
1936 PIO_STATUS_BLOCK pio
= aio
? (PIO_STATUS_BLOCK
) &aiocb
->aio_wincb
: &io
;
1938 /* If existing prw_handle asyncness doesn't match this call's, re-open */
1939 if (prw_handle
&& (prw_handle_isasync
!= !!aio
))
1940 NtClose (prw_handle
), prw_handle
= NULL
;
1942 /* If the file system supports sparse files and the application is
1943 writing beyond EOF spanning more than one sparsifiable chunk,
1944 convert the file to a sparse file. */
1945 if (pc
.support_sparse ()
1946 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE
)
1947 && NT_SUCCESS (NtQueryInformationFile (get_handle (),
1948 &io
, &fsi
, sizeof fsi
,
1949 FileStandardInformation
))
1950 && span_sparse_chunk (offset
, fsi
.EndOfFile
.QuadPart
))
1953 status
= NtFsControlFile (get_handle (), NULL
, NULL
, NULL
,
1954 &io
, FSCTL_SET_SPARSE
, NULL
, 0, NULL
, 0);
1955 if (NT_SUCCESS (status
))
1956 pc
.file_attributes (pc
.file_attributes ()
1957 | FILE_ATTRIBUTE_SPARSE_FILE
);
1958 debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1959 status
, pc
.get_nt_native_path ());
1961 if (!prw_handle
&& prw_open (true, aio
))
1963 status
= NtWriteFile (prw_handle
, evt
, NULL
, NULL
, pio
, buf
, count
,
1965 if (!NT_SUCCESS (status
))
1967 __seterrno_from_nt_status (status
);
1970 res
= aio
? (ssize_t
) aiocb
->aio_wincb
.info
: io
.Information
;
1976 /* Text mode stays slow and non-atomic. */
1977 off_t curpos
= lseek (0, SEEK_CUR
);
1978 if (curpos
< 0 || lseek (offset
, SEEK_SET
) < 0)
1982 res
= (ssize_t
) write (buf
, count
);
1983 if (lseek (curpos
, SEEK_SET
) < 0)
1987 /* If this was a disallowed async request, simulate its conclusion */
1990 aiocb
->aio_rbytes
= res
;
1991 aiocb
->aio_errno
= res
== -1 ? get_errno () : 0;
1992 SetEvent ((HANDLE
) aiocb
->aio_wincb
.event
);
1996 debug_printf ("%d = pwrite(%p, %ld, %D, %p)\n", res
, buf
, count
, offset
, aio
);
2001 fhandler_disk_file::mkdir (mode_t mode
)
2004 SECURITY_ATTRIBUTES sa
= sec_none_nih
;
2007 OBJECT_ATTRIBUTES attr
;
2009 PFILE_FULL_EA_INFORMATION p
= NULL
;
2011 ULONG access
= FILE_LIST_DIRECTORY
| SYNCHRONIZE
;
2013 if (pc
.fs_is_nfs ())
2015 /* When creating a dir on an NFS share, we have to set the
2016 file mode by writing a NFS fattr3 structure with the
2017 correct mode bits set. */
2018 plen
= sizeof (FILE_FULL_EA_INFORMATION
) + sizeof (NFS_V3_ATTR
)
2020 p
= (PFILE_FULL_EA_INFORMATION
) alloca (plen
);
2021 p
->NextEntryOffset
= 0;
2023 p
->EaNameLength
= sizeof (NFS_V3_ATTR
) - 1;
2024 p
->EaValueLength
= sizeof (fattr3
);
2025 strcpy (p
->EaName
, NFS_V3_ATTR
);
2026 fattr3
*nfs_attr
= (fattr3
*) (p
->EaName
+ p
->EaNameLength
+ 1);
2027 memset (nfs_attr
, 0, sizeof (fattr3
));
2028 nfs_attr
->type
= NF3DIR
;
2029 nfs_attr
->mode
= (mode
& 07777) & ~cygheap
->umask
;
2031 else if (has_acls () && !isremote ())
2032 /* If the filesystem supports ACLs, we will overwrite the DACL after the
2033 call to NtCreateFile. This requires a handle with READ_CONTROL and
2034 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
2035 open the file again.
2036 FIXME: On remote NTFS shares open sometimes fails because even the
2037 creator of the file doesn't have the right to change the DACL.
2038 I don't know what setting that is or how to recognize such a share,
2039 so for now we don't request WRITE_DAC on remote drives. */
2040 access
|= READ_CONTROL
| WRITE_DAC
;
2041 /* Setting case sensitivity requires FILE_WRITE_ATTRIBUTES. */
2042 if (wincap
.has_case_sensitive_dirs ()
2043 && !pc
.isremote () && pc
.fs_is_ntfs ())
2044 access
|= FILE_WRITE_ATTRIBUTES
;
2045 status
= NtCreateFile (&dir
, access
, pc
.get_object_attr (attr
, sa
), &io
, NULL
,
2046 FILE_ATTRIBUTE_DIRECTORY
, FILE_SHARE_VALID_FLAGS
,
2048 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
2049 | FILE_OPEN_FOR_BACKUP_INTENT
,
2051 if (NT_SUCCESS (status
))
2053 /* Set the "directory attribute" so that pc.isdir() returns correct
2054 value in subsequent function calls. */
2055 pc
.file_attributes (FILE_ATTRIBUTE_DIRECTORY
);
2057 set_created_file_access (dir
, pc
, mode
& 07777);
2059 /* FIXME: This default behaviour badly breaks interoperability.
2060 Inspecting the content of case-sensitive directories
2061 on remote machines results in lots of errors like
2062 disappearing diretories and files, file not found, etc. */
2064 /* Starting with Windows 10 1803, try to create all dirs below the
2065 installation root as case-sensitive. If STATUS_NOT_SUPPORTED
2066 is returned, WSL isn't installed (unfortunately a requirement
2067 for this functionality. */
2068 if (wincap
.has_case_sensitive_dirs ()
2069 && !pc
.isremote () && pc
.fs_is_ntfs ())
2071 PUNICODE_STRING new_dir
= pc
.get_nt_native_path ();
2072 PUNICODE_STRING root_dir
= &cygheap
->installation_root
;
2074 if (RtlEqualUnicodePathPrefix (new_dir
, root_dir
, TRUE
)
2075 && new_dir
->Buffer
[root_dir
->Length
/ sizeof (WCHAR
)] == L
'\\')
2077 FILE_CASE_SENSITIVE_INFORMATION fcsi
;
2079 fcsi
.Flags
= FILE_CS_FLAG_CASE_SENSITIVE_DIR
;
2080 status
= NtSetInformationFile (dir
, &io
, &fcsi
, sizeof fcsi
,
2081 FileCaseSensitiveInformation
);
2082 if (!NT_SUCCESS (status
))
2084 debug_printf ("Setting dir case sensitivity, status %y",
2086 if (status
== STATUS_NOT_SUPPORTED
)
2088 debug_printf ("Dir case sensitivity requires WSL");
2089 wincap
.disable_case_sensitive_dirs ();
2099 __seterrno_from_nt_status (status
);
2105 fhandler_disk_file::rmdir ()
2109 set_errno (ENOTDIR
);
2118 NTSTATUS status
= unlink_nt (pc
, false);
2120 if (!NT_SUCCESS (status
))
2122 __seterrno_from_nt_status (status
);
2128 /* This is the minimal number of entries which fit into the readdir cache.
2129 The number of bytes allocated by the cache is determined by this number,
2130 To tune caching, just tweak this number. To get a feeling for the size,
2131 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
2133 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
2135 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
2136 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
2137 + (NAME_MAX + 1) * sizeof (WCHAR)))
2141 char __cache
[DIR_BUF_SIZE
];
2145 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
2146 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
2148 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
2151 fhandler_disk_file::opendir (int fd
)
2156 if ((dir
= (DIR *) malloc (sizeof (DIR))) == NULL
)
2158 else if ((dir
->__d_dirname
= (char *) malloc ( sizeof (struct __DIR_cache
)))
2164 else if ((dir
->__d_dirent
=
2165 (struct dirent
*) malloc (sizeof (struct dirent
))) == NULL
)
2173 if (cfd
< 0 && fd
< 0)
2176 dir
->__d_dirent
->__d_version
= __DIRENT_VERSION
;
2177 dir
->__d_cookie
= __DIRENT_COOKIE
;
2178 dir
->__handle
= INVALID_HANDLE_VALUE
;
2179 dir
->__d_position
= 0;
2180 dir
->__flags
= (get_name ()[0] == '/' && get_name ()[1] == '\0')
2181 ? dirent_isroot
: 0;
2182 dir
->__d_internal
= 0;
2184 if (pc
.iscygdrive ())
2186 if (fd
< 0 && !open (O_RDONLY
, 0))
2191 dir
->__d_internal
= (uintptr_t) new __DIR_mounts (get_name ());
2192 d_cachepos (dir
) = 0;
2195 /* opendir() case. Initialize with given directory name and
2196 NULL directory handle. */
2197 OBJECT_ATTRIBUTES attr
;
2200 /* Tools like ls(1) call dirfd() to fetch the directory
2201 descriptor for calls to facl or fstat. The tight access mask
2202 used so far is not sufficient to reuse the handle for these
2203 calls, instead the facl/fstat calls find the handle to be
2204 unusable and have to re-open the file for reading attributes
2205 and control data. So, what we do here is to try to open the
2206 directory with more relaxed access mask which enables to use
2207 the handle for the aforementioned purpose. This should work
2208 in almost all cases. Only if it doesn't work due to
2209 permission problems, we drop the additional access bits and
2211 ACCESS_MASK fstat_mask
= READ_CONTROL
| FILE_READ_ATTRIBUTES
;
2215 status
= NtOpenFile (&get_handle (),
2216 SYNCHRONIZE
| FILE_LIST_DIRECTORY
2218 pc
.get_object_attr (attr
, sec_none_nih
),
2219 &io
, FILE_SHARE_VALID_FLAGS
,
2220 FILE_SYNCHRONOUS_IO_NONALERT
2221 | FILE_OPEN_FOR_BACKUP_INTENT
2222 | FILE_DIRECTORY_FILE
);
2223 if (!NT_SUCCESS (status
))
2225 if (status
== STATUS_ACCESS_DENIED
&& fstat_mask
)
2229 __seterrno_from_nt_status (status
);
2234 while (!NT_SUCCESS (status
));
2237 /* FileIdBothDirectoryInformation was unsupported on XP when
2238 accessing UDF. It's not clear if the call isn't also unsupported
2239 on other OS/FS combinations. Instead of testing for yet another
2240 error code, use FileIdBothDirectoryInformation only on FSes
2241 supporting persistent ACLs.
2243 NFS clients hide dangling symlinks from directory queries,
2244 unless you use the FileNamesInformation info class.
2245 FileIdBothDirectoryInformation works fine, but only if the NFS
2246 share is mounted to a drive letter. TODO: We don't test that
2247 here for now, but it might be worth to test if there's a speed
2248 gain in using FileIdBothDirectoryInformation, because it doesn't
2249 require to open the file to read the inode number. */
2250 if (pc
.hasgood_inode ())
2252 dir
->__flags
|= dirent_set_d_ino
;
2253 if (pc
.fs_is_nfs ())
2254 dir
->__flags
|= dirent_nfs_d_ino
;
2255 else if (!pc
.has_buggy_fileid_dirinfo ())
2256 dir
->__flags
|= dirent_get_d_ino
;
2263 /* Filling cfd with `this' (aka storing this in the file
2264 descriptor table should only happen after it's clear that
2265 opendir doesn't fail, otherwise we end up cfree'ing the
2266 fhandler twice, once in opendir() in dir.cc, the second
2267 time on exit. Nasty, nasty... */
2271 set_close_on_exec (true);
2276 syscall_printf ("%p = opendir (%s)", res
, get_name ());
2280 delete d_mounts (dir
);
2282 free (dir
->__d_dirent
);
2284 free (dir
->__d_dirname
);
2291 readdir_get_ino (const char *path
, bool dot_dot
)
2296 OBJECT_ATTRIBUTES attr
;
2302 fname
= (char *) alloca (strlen (path
) + 4);
2303 char *c
= stpcpy (fname
, path
);
2309 path_conv
pc (path
, PC_SYM_NOFOLLOW
| PC_POSIX
| PC_KEEP_HANDLE
);
2310 if (pc
.isspecial ())
2312 if (!stat_worker (pc
, &st
))
2315 else if (!pc
.hasgood_inode ())
2316 ino
= hash_path_name (0, pc
.get_nt_native_path ());
2317 else if ((hdl
= pc
.handle ()) != NULL
2318 || NT_SUCCESS (NtOpenFile (&hdl
, READ_CONTROL
,
2319 pc
.get_object_attr (attr
, sec_none_nih
),
2320 &io
, FILE_SHARE_VALID_FLAGS
,
2322 | FILE_OPEN_FOR_BACKUP_INTENT
2323 | (pc
.is_known_reparse_point ()
2324 ? FILE_OPEN_REPARSE_POINT
: 0)))
2327 ino
= pc
.get_ino_by_handle (hdl
);
2329 ino
= hash_path_name (0, pc
.get_nt_native_path ());
2335 fhandler_disk_file::readdir_helper (DIR *dir
, dirent
*de
, DWORD w32_err
,
2336 DWORD attr
, PUNICODE_STRING fname
)
2340 switch (d_mounts (dir
)->check_missing_mount (fname
))
2342 case __DIR_mount_none
:
2344 return geterrno_from_win_error (w32_err
);
2345 case __DIR_mount_virt_target
:
2346 de
->d_type
= DT_DIR
;
2352 dir
->__flags
&= ~dirent_set_d_ino
;
2355 /* Set d_type if type can be determined from file attributes. For .lnk
2356 symlinks, d_type will be reset below. Reparse points can be NTFS
2357 symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
2358 if (attr
&& !(attr
& ~FILE_ATTRIBUTE_VALID_FLAGS
))
2360 if (attr
& FILE_ATTRIBUTE_DIRECTORY
)
2361 de
->d_type
= DT_DIR
;
2362 /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
2363 else if (!(attr
& FILE_ATTRIBUTE_SYSTEM
))
2364 de
->d_type
= DT_REG
;
2367 /* Check for reparse points that can be treated as posix symlinks.
2368 Mountpoints and unknown or unhandled reparse points will be treated
2369 as normal file/directory/unknown. In all cases, returning the INO of
2370 the reparse point (not of the target) matches behavior of posix systems.
2371 Unless the file is OFFLINE. *.
2373 if ((attr
& FILE_ATTRIBUTE_REPARSE_POINT
) && !isoffline (attr
))
2375 OBJECT_ATTRIBUTES oattr
;
2377 InitializeObjectAttributes (&oattr
, fname
, pc
.objcaseinsensitive (),
2378 get_handle (), NULL
);
2379 if (readdir_check_reparse_point (&oattr
, isremote ()))
2380 de
->d_type
= DT_LNK
;
2383 /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
2384 .lnk suffix and set d_type accordingly. */
2385 if ((attr
& (FILE_ATTRIBUTE_DIRECTORY
2386 | FILE_ATTRIBUTE_REPARSE_POINT
2387 | FILE_ATTRIBUTE_READONLY
)) == FILE_ATTRIBUTE_READONLY
2388 && fname
->Length
> 4 * sizeof (WCHAR
))
2390 UNICODE_STRING uname
;
2392 RtlInitCountedUnicodeString (&uname
,
2394 + fname
->Length
/ sizeof (WCHAR
) - 4,
2395 4 * sizeof (WCHAR
));
2396 if (RtlEqualUnicodeString (&uname
, &ro_u_lnk
, TRUE
))
2399 char *file
= tp
.c_get ();
2400 char *p
= stpcpy (file
, pc
.get_posix ());
2403 sys_wcstombs (p
, NT_MAX_PATH
- (p
- file
),
2404 fname
->Buffer
, fname
->Length
/ sizeof (WCHAR
));
2405 path_conv
fpath (file
, PC_SYM_NOFOLLOW
);
2406 if (fpath
.issymlink ())
2408 fname
->Length
-= 4 * sizeof (WCHAR
);
2409 de
->d_type
= DT_LNK
;
2411 else if (fpath
.isfifo ())
2413 fname
->Length
-= 4 * sizeof (WCHAR
);
2414 de
->d_type
= DT_FIFO
;
2416 else if (fpath
.is_fs_special ())
2418 fname
->Length
-= 4 * sizeof (WCHAR
);
2419 de
->d_type
= S_ISCHR (fpath
.dev
.mode ()) ? DT_CHR
: DT_BLK
;
2424 sys_wcstombs (de
->d_name
, NAME_MAX
+ 1, fname
->Buffer
,
2425 fname
->Length
/ sizeof (WCHAR
));
2427 /* Don't try to optimize relative to dir->__d_position. On several
2428 filesystems it's no safe bet that "." and ".." entries always
2430 if (de
->d_name
[0] == '.')
2432 if (de
->d_name
[1] == '\0')
2433 dir
->__flags
|= dirent_saw_dot
;
2434 else if (de
->d_name
[1] == '.' && de
->d_name
[2] == '\0')
2435 dir
->__flags
|= dirent_saw_dot_dot
;
2441 fhandler_disk_file::readdir (DIR *dir
, dirent
*de
)
2444 NTSTATUS status
= STATUS_SUCCESS
;
2445 PFILE_ID_BOTH_DIR_INFORMATION buf
= NULL
;
2447 ULONG FileNameLength
;
2448 ULONG FileAttributes
;
2450 UNICODE_STRING fname
;
2452 /* d_cachepos always refers to the next cache entry to use. If it's 0
2453 we must reload the cache. */
2455 if (d_cachepos (dir
) == 0)
2457 if ((dir
->__flags
& dirent_get_d_ino
))
2459 status
= NtQueryDirectoryFile (get_handle (), NULL
, NULL
, NULL
, &io
,
2460 d_cache (dir
), DIR_BUF_SIZE
,
2461 FileIdBothDirectoryInformation
,
2462 FALSE
, NULL
, dir
->__d_position
== 0);
2463 /* FileIdBothDirectoryInformation isn't supported on some
2464 remote drives, but we don't know every system out there.
2465 Check various status codes indicating this. */
2466 if (!NT_SUCCESS (status
)
2467 && (status
== STATUS_INVALID_LEVEL
2468 || status
== STATUS_NOT_SUPPORTED
2469 || status
== STATUS_INVALID_PARAMETER
2470 || status
== STATUS_INVALID_NETWORK_RESPONSE
2471 || status
== STATUS_INVALID_INFO_CLASS
))
2472 dir
->__flags
&= ~dirent_get_d_ino
;
2474 /* NFS must use FileNamesInformation! Any other information class
2475 skips all symlinks. */
2476 if (!(dir
->__flags
& dirent_get_d_ino
))
2477 status
= NtQueryDirectoryFile (get_handle (), NULL
, NULL
, NULL
, &io
,
2478 d_cache (dir
), DIR_BUF_SIZE
,
2479 (dir
->__flags
& dirent_nfs_d_ino
)
2480 ? FileNamesInformation
2481 : FileBothDirectoryInformation
,
2482 FALSE
, NULL
, dir
->__d_position
== 0);
2485 if (status
== STATUS_NO_MORE_FILES
)
2487 else if (!NT_SUCCESS (status
))
2488 debug_printf ("NtQueryDirectoryFile failed, status %y, win32 error %u",
2489 status
, RtlNtStatusToDosError (status
));
2492 buf
= (PFILE_ID_BOTH_DIR_INFORMATION
) (d_cache (dir
) + d_cachepos (dir
));
2493 if (buf
->NextEntryOffset
== 0)
2494 d_cachepos (dir
) = 0;
2496 d_cachepos (dir
) += buf
->NextEntryOffset
;
2497 if ((dir
->__flags
& dirent_get_d_ino
))
2499 FileName
= buf
->FileName
;
2500 FileNameLength
= buf
->FileNameLength
;
2501 FileAttributes
= buf
->FileAttributes
;
2502 if ((dir
->__flags
& dirent_set_d_ino
))
2503 de
->d_ino
= buf
->FileId
.QuadPart
;
2505 else if ((dir
->__flags
& dirent_nfs_d_ino
))
2507 FileName
= ((PFILE_NAMES_INFORMATION
) buf
)->FileName
;
2508 FileNameLength
= ((PFILE_NAMES_INFORMATION
) buf
)->FileNameLength
;
2512 FileName
= ((PFILE_BOTH_DIR_INFORMATION
) buf
)->FileName
;
2514 ((PFILE_BOTH_DIR_INFORMATION
) buf
)->FileNameLength
;
2516 ((PFILE_BOTH_DIR_INFORMATION
) buf
)->FileAttributes
;
2518 RtlInitCountedUnicodeString (&fname
, FileName
, FileNameLength
);
2519 d_mounts (dir
)->check_mount (&fname
);
2520 if (de
->d_ino
== 0 && (dir
->__flags
& dirent_set_d_ino
))
2522 /* Don't try to optimize relative to dir->__d_position. On several
2523 filesystems it's no safe bet that "." and ".." entries always
2525 if (FileNameLength
== sizeof (WCHAR
) && FileName
[0] == '.')
2526 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2527 else if (FileNameLength
== 2 * sizeof (WCHAR
)
2528 && FileName
[0] == L
'.' && FileName
[1] == L
'.')
2530 if (!(dir
->__flags
& dirent_isroot
))
2531 de
->d_ino
= readdir_get_ino (get_name (), true);
2533 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2537 OBJECT_ATTRIBUTES attr
;
2541 InitializeObjectAttributes (&attr
, &fname
,
2542 pc
.objcaseinsensitive (),
2543 get_handle (), NULL
);
2544 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2545 NtOpenFile here returns the inode number of the symlink target,
2546 rather than the inode number of the symlink itself.
2548 Worse, trying to open a symlink without setting the special
2549 "ActOnSymlink" EA triggers a bug in Windows 7 which results
2550 in a timeout of up to 20 seconds, followed by two exceptions
2553 Since both results are far from desirable, we open symlinks
2554 on NFS so that we get the right inode and a happy W7.
2555 And, since some filesystems choke on the EAs, we don't
2556 use them unconditionally. */
2557 f_status
= (dir
->__flags
& dirent_nfs_d_ino
)
2558 ? NtCreateFile (&hdl
,
2559 READ_CONTROL
| FILE_READ_ATTRIBUTES
,
2560 &attr
, &io
, NULL
, 0,
2561 FILE_SHARE_VALID_FLAGS
, FILE_OPEN
,
2562 FILE_OPEN_FOR_BACKUP_INTENT
,
2563 &nfs_aol_ffei
, sizeof nfs_aol_ffei
)
2564 : NtOpenFile (&hdl
, READ_CONTROL
, &attr
, &io
,
2565 FILE_SHARE_VALID_FLAGS
,
2567 | FILE_OPEN_FOR_BACKUP_INTENT
2568 | FILE_OPEN_REPARSE_POINT
);
2569 if (NT_SUCCESS (f_status
))
2571 /* We call NtQueryInformationFile here, rather than
2572 pc.get_ino_by_handle(), otherwise we can't short-circuit
2573 dirent_set_d_ino correctly. */
2574 FILE_INTERNAL_INFORMATION fii
;
2575 f_status
= NtQueryInformationFile (hdl
, &io
, &fii
, sizeof fii
,
2576 FileInternalInformation
);
2577 /* On NFS fetch the (faked, but useful) DOS attribute.
2578 We need it to recognize shortcut FIFOs. */
2579 if ((dir
->__flags
& dirent_nfs_d_ino
))
2581 FILE_BASIC_INFORMATION fbi
;
2583 if (NT_SUCCESS (NtQueryInformationFile (hdl
, &io
, &fbi
,
2584 sizeof fbi
, FileBasicInformation
)))
2585 FileAttributes
= fbi
.FileAttributes
;
2588 if (NT_SUCCESS (f_status
))
2590 if (pc
.isgood_inode (fii
.IndexNumber
.QuadPart
))
2591 de
->d_ino
= fii
.IndexNumber
.QuadPart
;
2593 /* Untrusted file system. Don't try to fetch inode
2595 dir
->__flags
&= ~dirent_set_d_ino
;
2602 if (!(res
= readdir_helper (dir
, de
, RtlNtStatusToDosError (status
),
2603 FileAttributes
, &fname
)))
2604 dir
->__d_position
++;
2605 else if (!(dir
->__flags
& dirent_saw_dot
))
2607 strcpy (de
->d_name
, ".");
2608 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2609 de
->d_type
= DT_DIR
;
2610 dir
->__d_position
++;
2611 dir
->__flags
|= dirent_saw_dot
;
2614 else if (!(dir
->__flags
& dirent_saw_dot_dot
))
2616 strcpy (de
->d_name
, "..");
2617 if (!(dir
->__flags
& dirent_isroot
))
2618 de
->d_ino
= readdir_get_ino (get_name (), true);
2620 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2621 de
->d_type
= DT_DIR
;
2622 dir
->__d_position
++;
2623 dir
->__flags
|= dirent_saw_dot_dot
;
2627 syscall_printf ("%d = readdir(%p, %p) (L\"%lS\" > \"%ls\") (attr %y > type %d)",
2628 res
, dir
, &de
, res
? NULL
: &fname
, res
? "***" : de
->d_name
,
2629 FileAttributes
, de
->d_type
);
2634 fhandler_disk_file::telldir (DIR *dir
)
2636 return dir
->__d_position
;
2640 fhandler_disk_file::seekdir (DIR *dir
, long loc
)
2643 while (loc
> dir
->__d_position
)
2644 if (!::readdir (dir
))
2649 fhandler_disk_file::rewinddir (DIR *dir
)
2651 d_cachepos (dir
) = 0;
2652 dir
->__d_position
= 0;
2653 d_mounts (dir
)->rewind ();
2657 fhandler_disk_file::closedir (DIR *dir
)
2661 delete d_mounts (dir
);
2662 syscall_printf ("%d = closedir(%p, %s)", res
, dir
, get_name ());
2667 fhandler_disk_file::fs_ioc_getflags ()
2671 FILE_BASIC_INFORMATION fbi
;
2672 FILE_CASE_SENSITIVE_INFORMATION fcsi
;
2675 status
= NtQueryInformationFile (get_handle (), &io
, &fbi
, sizeof fbi
,
2676 FileBasicInformation
);
2677 if (NT_SUCCESS (status
))
2679 flags
= (uint64_t) fbi
.FileAttributes
& FS_FL_USER_VISIBLE
;
2680 pc
.file_attributes (fbi
.FileAttributes
);
2683 flags
= (uint64_t) pc
.file_attributes () & FS_FL_USER_VISIBLE
;
2684 if (pc
.isdir () && wincap
.has_case_sensitive_dirs ()
2685 && !pc
.isremote () && pc
.fs_is_ntfs ())
2688 status
= NtQueryInformationFile (get_handle (), &io
,
2690 FileCaseSensitiveInformation
);
2691 if (NT_SUCCESS (status
)
2692 && (fcsi
.Flags
& FILE_CS_FLAG_CASE_SENSITIVE_DIR
))
2693 flags
|= FS_CASESENS_FL
;
2698 /* Settable DOS attributes */
2699 #define FS_FL_SETATTRIBS (FS_READONLY_FL \
2709 fhandler_disk_file::fs_ioc_setflags (uint64_t flags
)
2715 OBJECT_ATTRIBUTES attr
;
2717 FILE_BASIC_INFORMATION fbi
;
2718 FILE_SET_SPARSE_BUFFER fssb
;
2720 FILE_CASE_SENSITIVE_INFORMATION fcsi
;
2722 if ((get_access () & (GENERIC_WRITE
| FILE_WRITE_ATTRIBUTES
)) != 0)
2726 status
= NtOpenFile (&fh
, FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
,
2727 pc
.init_reopen_attr (attr
, get_handle ()), &io
,
2728 FILE_SHARE_VALID_FLAGS
,
2729 FILE_OPEN_FOR_BACKUP_INTENT
);
2730 if (!NT_SUCCESS (status
))
2733 __seterrno_from_nt_status (status
);
2737 old_flags
= fs_ioc_getflags ();
2738 if ((old_flags
& FS_FL_SETATTRIBS
) != (flags
& FS_FL_SETATTRIBS
))
2740 fbi
.CreationTime
.QuadPart
2741 = fbi
.LastAccessTime
.QuadPart
2742 = fbi
.LastWriteTime
.QuadPart
2743 = fbi
.ChangeTime
.QuadPart
= 0LL;
2744 fbi
.FileAttributes
= (ULONG
) old_flags
;
2745 fbi
.FileAttributes
&= ~FS_FL_SETATTRIBS
;
2746 fbi
.FileAttributes
|= (flags
& FS_FL_SETATTRIBS
);
2747 if (fbi
.FileAttributes
== 0)
2748 fbi
.FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
2749 status
= NtSetInformationFile (fh
, &io
, &fbi
, sizeof fbi
,
2750 FileBasicInformation
);
2751 if (!NT_SUCCESS (status
))
2753 __seterrno_from_nt_status (status
);
2757 if (!pc
.isdir() && (flags
& FS_SPARSE_FL
) != (old_flags
& FS_SPARSE_FL
))
2759 fssb
.SetSparse
= (flags
& FS_SPARSE_FL
) ? TRUE
: FALSE
;
2760 status
= NtFsControlFile (fh
, NULL
, NULL
, NULL
, &io
,
2761 FSCTL_SET_SPARSE
, &fssb
, sizeof fssb
, NULL
, 0);
2762 if (!NT_SUCCESS (status
))
2764 __seterrno_from_nt_status (status
);
2768 if (pc
.isdir () && (flags
& FS_CASESENS_FL
) != (old_flags
& FS_CASESENS_FL
))
2770 if (wincap
.has_case_sensitive_dirs ()
2771 && !pc
.isremote () && pc
.fs_is_ntfs ())
2773 fcsi
.Flags
= (flags
& FS_CASESENS_FL
)
2774 ? FILE_CS_FLAG_CASE_SENSITIVE_DIR
: 0;
2775 status
= NtSetInformationFile (fh
, &io
, &fcsi
, sizeof fcsi
,
2776 FileCaseSensitiveInformation
);
2777 if (!NT_SUCCESS (status
))
2779 __seterrno_from_nt_status (status
);
2785 set_errno (ENOTSUP
);
2789 if ((flags
& FS_COMPRESSED_FL
) != (old_flags
& FS_COMPRESSED_FL
))
2791 if (fh
!= get_handle ())
2794 if ((get_access () & (GENERIC_WRITE
| GENERIC_READ
))
2795 != (GENERIC_WRITE
| GENERIC_READ
))
2797 status
= NtOpenFile (&fh
, GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
2798 pc
.init_reopen_attr (attr
, get_handle ()), &io
,
2799 FILE_SHARE_VALID_FLAGS
,
2800 FILE_SYNCHRONOUS_IO_NONALERT
2801 | FILE_OPEN_FOR_BACKUP_INTENT
);
2802 if (!NT_SUCCESS (status
))
2805 __seterrno_from_nt_status (status
);
2809 comp
= (flags
& FS_COMPRESSED_FL
)
2810 ? COMPRESSION_FORMAT_DEFAULT
: COMPRESSION_FORMAT_NONE
;
2811 status
= NtFsControlFile (fh
, NULL
, NULL
, NULL
, &io
,
2812 FSCTL_SET_COMPRESSION
, &comp
, sizeof comp
,
2814 if (!NT_SUCCESS (status
))
2816 __seterrno_from_nt_status (status
);
2820 if (!pc
.isdir() && (flags
& FS_ENCRYPT_FL
) != (old_flags
& FS_ENCRYPT_FL
))
2823 PWCHAR path
= tp
.w_get ();
2826 /* EncryptFileW/DecryptFileW needs exclusive access. */
2827 if (fh
!= get_handle ())
2829 NtClose (get_handle ());
2832 pc
.get_wide_win32_path (path
);
2833 cret
= (flags
& FS_ENCRYPT_FL
)
2834 ? EncryptFileW (path
) : DecryptFileW (path
, 0);
2835 status
= NtOpenFile (&fh
, get_access (),
2836 pc
.get_object_attr (attr
, sec_none_nih
), &io
,
2837 FILE_SHARE_VALID_FLAGS
,
2838 FILE_SYNCHRONOUS_IO_NONALERT
2839 | FILE_OPEN_FOR_BACKUP_INTENT
);
2840 if (!NT_SUCCESS (status
))
2842 __seterrno_from_nt_status (status
);
2854 status
= NtQueryInformationFile (fh
, &io
, &fbi
, sizeof fbi
,
2855 FileBasicInformation
);
2856 if (NT_SUCCESS (status
))
2857 pc
.file_attributes (fbi
.FileAttributes
);
2858 if (fh
!= get_handle ())
2864 fhandler_disk_file::ioctl (unsigned int cmd
, void *p
)
2871 case FS_IOC_GETFLAGS
:
2874 uint64_t *fp
= (uint64_t *) p
;
2875 *fp
= fs_ioc_getflags ();
2878 __except (EFAULT
) {}
2881 case FS_IOC_SETFLAGS
:
2884 flags
= *(__uint64_t
*) p
;
2891 if (flags
& ~FS_FL_USER_MODIFIABLE
)
2896 ret
= fs_ioc_setflags (flags
);
2899 ret
= fhandler_base::ioctl (cmd
, p
);
2902 syscall_printf ("%d = ioctl_file(%x, %p)", ret
, cmd
, p
);