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 /* The inode numbers returned from a remote NT4 NTFS are ephemeral
155 /* Starting with version 3.5.4, Samba returns the real inode numbers, if
156 the file is on the same device as the root of the share (Samba function
157 get_FileIndex). 32 bit inode numbers returned by older versions (likely
158 < 3.0) are ephemeral. */
159 if (fs_is_samba () && fs
.samba_version () < 0x03050400)
161 /* Otherwise, trust the inode numbers unless proved otherwise. */
165 /* Check reparse point to determine if it should be treated as a
166 posix symlink or as a normal file/directory. Logic is explained
167 in detail in check_reparse_point_target in path.cc. */
169 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr
, bool remote
)
175 UNICODE_STRING symbuf
;
178 status
= NtOpenFile (&reph
, READ_CONTROL
, attr
, &io
, FILE_SHARE_VALID_FLAGS
,
179 FILE_OPEN_FOR_BACKUP_INTENT
| FILE_OPEN_REPARSE_POINT
);
180 if (NT_SUCCESS (status
))
182 PREPARSE_DATA_BUFFER rp
= (PREPARSE_DATA_BUFFER
) tp
.c_get ();
183 ret
= (check_reparse_point_target (reph
, remote
, rp
, &symbuf
) > 0);
190 path_conv::get_ino_by_handle (HANDLE hdl
)
193 FILE_INTERNAL_INFORMATION fii
;
195 if (NT_SUCCESS (NtQueryInformationFile (hdl
, &io
, &fii
, sizeof fii
,
196 FileInternalInformation
))
197 && isgood_inode (fii
.IndexNumber
.QuadPart
))
198 return fii
.IndexNumber
.QuadPart
;
202 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
203 This returns the content of a struct fattr3 as defined in RFC 1813.
204 The content is the NFS equivalent of struct stat. so there's not much
205 to do here except for copying. */
207 fhandler_base::fstat_by_nfs_ea (struct stat
*buf
)
209 fattr3
*nfs_attr
= pc
.nfsattr ();
212 bool ldap_open
= false;
214 /* NFS stumbles over its own caching. If you write to the file,
215 a subsequent fstat does not return the actual size of the file,
216 but the size at the time the handle has been opened. Unless
217 access through another handle invalidates the caching within the
218 NFS client. Skip this for Cygwin-created Symlinks playing FIFOs
219 (this sets the filler1 member to NF3FIFO). */
220 if (get_handle () && nfs_attr
->filler1
!= NF3FIFO
)
222 if (get_access () & GENERIC_WRITE
)
223 FlushFileBuffers (get_handle ());
224 pc
.get_finfo (get_handle ());
226 buf
->st_dev
= nfs_attr
->fsid
;
227 buf
->st_ino
= nfs_attr
->fileid
;
228 buf
->st_mode
= (nfs_attr
->mode
& 0xfff)
229 | nfs_type_mapping
[nfs_attr
->type
& 7];
230 buf
->st_nlink
= nfs_attr
->nlink
;
231 if (cygheap
->pg
.nss_pwd_db ())
233 /* Try to map UNIX uid/gid to Cygwin uid/gid. If there's no mapping in
234 the cache, try to fetch it from the configured RFC 2307 domain (see
235 last comment in cygheap_domain_info::init() for more information) and
236 add it to the mapping cache. */
237 buf
->st_uid
= cygheap
->ugid_cache
.get_uid (nfs_attr
->uid
);
238 if (buf
->st_uid
== ILLEGAL_UID
)
240 uid_t map_uid
= ILLEGAL_UID
;
242 domain
= cygheap
->dom
.get_rfc2307_domain ();
243 if ((ldap_open
= (cldap
.open (domain
) == NO_ERROR
)))
244 map_uid
= cldap
.remap_uid (nfs_attr
->uid
);
245 if (map_uid
== ILLEGAL_UID
)
246 map_uid
= MAP_UNIX_TO_CYGWIN_ID (nfs_attr
->uid
);
247 cygheap
->ugid_cache
.add_uid (nfs_attr
->uid
, map_uid
);
248 buf
->st_uid
= map_uid
;
251 else /* fake files being owned by current user. */
252 buf
->st_uid
= myself
->uid
;
253 if (cygheap
->pg
.nss_grp_db ())
256 buf
->st_gid
= cygheap
->ugid_cache
.get_gid (nfs_attr
->gid
);
257 if (buf
->st_gid
== ILLEGAL_GID
)
259 gid_t map_gid
= ILLEGAL_GID
;
261 domain
= cygheap
->dom
.get_rfc2307_domain ();
262 if ((ldap_open
|| cldap
.open (domain
) == NO_ERROR
))
263 map_gid
= cldap
.remap_gid (nfs_attr
->gid
);
264 if (map_gid
== ILLEGAL_GID
)
265 map_gid
= MAP_UNIX_TO_CYGWIN_ID (nfs_attr
->gid
);
266 cygheap
->ugid_cache
.add_gid (nfs_attr
->gid
, map_gid
);
267 buf
->st_gid
= map_gid
;
270 else /* fake files being owned by current group. */
271 buf
->st_gid
= myself
->gid
;
272 buf
->st_rdev
= makedev (nfs_attr
->rdev
.specdata1
,
273 nfs_attr
->rdev
.specdata2
);
274 buf
->st_size
= nfs_attr
->size
;
275 buf
->st_blksize
= PREFERRED_IO_BLKSIZE
;
276 buf
->st_blocks
= (nfs_attr
->used
+ S_BLKSIZE
- 1) / S_BLKSIZE
;
277 buf
->st_atim
.tv_sec
= nfs_attr
->atime
.tv_sec
;
278 buf
->st_atim
.tv_nsec
= nfs_attr
->atime
.tv_nsec
;
279 buf
->st_mtim
.tv_sec
= nfs_attr
->mtime
.tv_sec
;
280 buf
->st_mtim
.tv_nsec
= nfs_attr
->mtime
.tv_nsec
;
281 buf
->st_ctim
.tv_sec
= nfs_attr
->ctime
.tv_sec
;
282 buf
->st_ctim
.tv_nsec
= nfs_attr
->ctime
.tv_nsec
;
287 fhandler_base::fstat_by_handle (struct stat
*buf
)
289 HANDLE h
= get_stat_handle ();
292 /* If the file has been opened for other purposes than stat, we can't rely
293 on the information stored in pc.fai. So we overwrite them here. */
296 status
= pc
.get_finfo (h
);
297 if (!NT_SUCCESS (status
))
299 debug_printf ("%y = NtQueryInformationFile(%S, FileAllInformation)",
300 status
, pc
.get_nt_native_path ());
304 if (pc
.isgood_inode (pc
.fai ()->InternalInformation
.IndexNumber
.QuadPart
))
305 ino
= pc
.fai ()->InternalInformation
.IndexNumber
.QuadPart
;
306 return fstat_helper (buf
);
310 fhandler_base::fstat_by_name (struct stat
*buf
)
313 OBJECT_ATTRIBUTES attr
;
315 UNICODE_STRING dirname
;
316 UNICODE_STRING basename
;
319 FILE_ID_BOTH_DIR_INFORMATION fdi
;
320 WCHAR buf
[NAME_MAX
+ 1];
323 if (!ino
&& pc
.hasgood_inode () && !pc
.has_buggy_fileid_dirinfo ())
325 RtlSplitUnicodePath (pc
.get_nt_native_path (), &dirname
, &basename
);
326 InitializeObjectAttributes (&attr
, &dirname
, pc
.objcaseinsensitive (),
328 status
= NtOpenFile (&dir
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
329 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
330 FILE_SYNCHRONOUS_IO_NONALERT
331 | FILE_OPEN_FOR_BACKUP_INTENT
332 | FILE_DIRECTORY_FILE
);
333 if (!NT_SUCCESS (status
))
334 debug_printf ("%y = NtOpenFile(%S)", status
,
335 pc
.get_nt_native_path ());
338 status
= NtQueryDirectoryFile (dir
, NULL
, NULL
, NULL
, &io
,
339 &fdi_buf
.fdi
, sizeof fdi_buf
,
340 FileIdBothDirectoryInformation
,
341 TRUE
, &basename
, TRUE
);
343 if (!NT_SUCCESS (status
))
344 debug_printf ("%y = NtQueryDirectoryFile(%S)", status
,
345 pc
.get_nt_native_path ());
347 ino
= fdi_buf
.fdi
.FileId
.QuadPart
;
350 return fstat_helper (buf
);
354 fhandler_base::fstat_fs (struct stat
*buf
)
358 int open_flags
= O_RDONLY
| O_BINARY
;
360 if (get_stat_handle ())
365 res
= fstat_by_nfs_ea (buf
);
366 else if (!is_fs_special () || get_flags () & O_PATH
)
367 res
= fstat_by_handle (buf
);
370 res
= fstat_by_name (buf
);
373 /* First try to open with generic read access. This allows to read the file
374 in fstat_helper (when checking for executability) without having to
375 re-open it. Opening a file can take a lot of time on network drives
376 so we try to avoid that. */
377 oret
= open_fs (open_flags
, 0);
380 query_open (query_read_attributes
);
381 oret
= open_fs (open_flags
, 0);
385 /* We now have a valid handle, regardless of the "nohandle" state.
386 Since fhandler_base::close only calls CloseHandle if !nohandle,
387 we have to set it to false before calling close and restore
388 the state afterwards. */
389 res
= pc
.fs_is_nfs () ? fstat_by_nfs_ea (buf
) : fstat_by_handle (buf
);
390 bool no_handle
= nohandle ();
393 nohandle (no_handle
);
397 res
= fstat_by_name (buf
);
403 fhandler_base::fstat_helper (struct stat
*buf
)
406 FILE_COMPRESSION_INFORMATION fci
;
407 HANDLE h
= get_stat_handle ();
408 PFILE_ALL_INFORMATION pfai
= pc
.fai ();
409 ULONG attributes
= pc
.file_attributes ();
411 to_timestruc_t (&pfai
->BasicInformation
.LastAccessTime
, &buf
->st_atim
);
412 to_timestruc_t (&pfai
->BasicInformation
.LastWriteTime
, &buf
->st_mtim
);
413 /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
414 (FAT for instance). If so, it's faked using LastWriteTime. */
415 to_timestruc_t (pfai
->BasicInformation
.ChangeTime
.QuadPart
416 ? &pfai
->BasicInformation
.ChangeTime
417 : &pfai
->BasicInformation
.LastWriteTime
,
419 to_timestruc_t (&pfai
->BasicInformation
.CreationTime
, &buf
->st_birthtim
);
420 buf
->st_dev
= get_dev ();
421 /* CV 2011-01-13: Observations on the Cygwin mailing list point to an
422 interesting behaviour in some Windows versions. Apparently the size of
423 a directory is computed at the time the directory is first scanned. This
424 can result in two subsequent NtQueryInformationFile calls to return size
425 0 in the first call and size > 0 in the second call. This in turn can
426 affect applications like newer tar.
427 FIXME: Is the allocation size affected as well? */
428 buf
->st_size
= pc
.isdir ()
430 : (off_t
) pfai
->StandardInformation
.EndOfFile
.QuadPart
;
431 /* The number of links to a directory includes the number of subdirectories
432 in the directory, since all those subdirectories point to it. However,
433 this is painfully slow, so we do without it. */
434 buf
->st_nlink
= pc
.fai()->StandardInformation
.NumberOfLinks
;
436 /* Enforce namehash as inode number on untrusted file systems. */
437 buf
->st_ino
= ino
?: get_ino ();
439 buf
->st_blksize
= PREFERRED_IO_BLKSIZE
;
441 if (buf
->st_size
== 0
442 && pfai
->StandardInformation
.AllocationSize
.QuadPart
== 0LL)
443 /* File is empty and no blocks are preallocated. */
445 else if (pfai
->StandardInformation
.AllocationSize
.QuadPart
> 0LL)
446 /* A successful NtQueryInformationFile returns the allocation size
447 correctly for compressed and sparse files as well.
448 Allocation size 0 is ignored here because (at least) Windows 10
449 1607 always returns 0 for CompactOS compressed files. */
450 buf
->st_blocks
= (pfai
->StandardInformation
.AllocationSize
.QuadPart
451 + S_BLKSIZE
- 1) / S_BLKSIZE
;
452 else if ((pfai
->StandardInformation
.AllocationSize
.QuadPart
== 0LL
453 || ::has_attribute (attributes
, FILE_ATTRIBUTE_COMPRESSED
454 | FILE_ATTRIBUTE_SPARSE_FILE
))
455 && h
&& !is_fs_special ()
456 && !NtQueryInformationFile (h
, &st
, (PVOID
) &fci
, sizeof fci
,
457 FileCompressionInformation
))
458 /* Otherwise we request the actual amount of bytes allocated for
459 compressed, sparsed and CompactOS files. */
460 buf
->st_blocks
= (fci
.CompressedFileSize
.QuadPart
+ S_BLKSIZE
- 1)
463 /* Otherwise compute no. of blocks from file size. */
464 buf
->st_blocks
= (buf
->st_size
+ S_BLKSIZE
- 1) / S_BLKSIZE
;
467 /* Using a side effect: get_file_attributes checks for directory.
468 This is used, to set S_ISVTX, if needed. */
470 buf
->st_mode
= S_IFDIR
;
471 else if (pc
.issymlink ())
473 buf
->st_size
= pc
.get_symlink_length ();
474 get_file_attribute (h
, pc
, buf
->st_mode
,
475 &buf
->st_uid
, &buf
->st_gid
);
476 /* symlinks are everything for everyone! */
477 buf
->st_mode
= S_IFLNK
| S_IRWXU
| S_IRWXG
| S_IRWXO
;
480 else if (pc
.issocket ())
481 buf
->st_mode
= S_IFSOCK
;
483 if (!get_file_attribute (h
, pc
, buf
->st_mode
, &buf
->st_uid
, &buf
->st_gid
))
485 /* If read-only attribute is set, modify ntsec return value */
486 if (::has_attribute (attributes
, FILE_ATTRIBUTE_READONLY
)
487 && !pc
.isdir () && !pc
.issymlink () && !pc
.is_fs_special ())
488 buf
->st_mode
&= ~(S_IWUSR
| S_IWGRP
| S_IWOTH
);
490 if (buf
->st_mode
& S_IFMT
)
492 else if (!is_fs_special ())
493 buf
->st_mode
|= S_IFREG
;
496 buf
->st_dev
= buf
->st_rdev
= dev ();
497 buf
->st_mode
= dev ().mode ();
503 buf
->st_mode
|= STD_RBITS
;
505 if (!::has_attribute (attributes
, FILE_ATTRIBUTE_READONLY
))
506 buf
->st_mode
|= STD_WBITS
;
507 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
510 buf
->st_mode
|= S_IFDIR
| STD_WBITS
| STD_XBITS
;
511 else if (buf
->st_mode
& S_IFMT
)
513 else if (is_fs_special ())
515 buf
->st_dev
= buf
->st_rdev
= dev ();
516 buf
->st_mode
= dev ().mode ();
521 buf
->st_mode
|= S_IFREG
;
522 /* Check suffix for executable file. */
523 if (pc
.exec_state () != is_executable
)
525 PUNICODE_STRING path
= pc
.get_nt_native_path ();
527 if (RtlEqualUnicodePathSuffix (path
, &ro_u_exe
, TRUE
)
528 || RtlEqualUnicodePathSuffix (path
, &ro_u_lnk
, TRUE
))
531 /* No known suffix, check file header. This catches binaries and
533 if (pc
.exec_state () == dont_know_if_executable
)
535 OBJECT_ATTRIBUTES attr
;
539 /* We have to re-open the file. Either the file is not opened
540 for reading, or the read will change the file position of the
542 status
= NtOpenFile (&h
, SYNCHRONIZE
| FILE_READ_DATA
,
543 pc
.init_reopen_attr (attr
, h
), &io
,
544 FILE_SHARE_VALID_FLAGS
,
545 FILE_OPEN_FOR_BACKUP_INTENT
546 | FILE_SYNCHRONOUS_IO_NONALERT
);
547 if (!NT_SUCCESS (status
))
548 debug_printf ("%y = NtOpenFile(%S)", status
,
549 pc
.get_nt_native_path ());
552 LARGE_INTEGER off
= { QuadPart
:0LL };
555 status
= NtReadFile (h
, NULL
, NULL
, NULL
,
556 &io
, magic
, 3, &off
, NULL
);
557 if (!NT_SUCCESS (status
))
558 debug_printf ("%y = NtReadFile(%S)", status
,
559 pc
.get_nt_native_path ());
560 else if (has_exec_chars (magic
, io
.Information
))
562 /* Heureka, it's an executable */
564 buf
->st_mode
|= STD_XBITS
;
570 if (pc
.exec_state () == is_executable
)
571 buf
->st_mode
|= STD_XBITS
;
573 /* This fakes the permissions of all files to match the current umask. */
574 buf
->st_mode
&= ~(cygheap
->umask
);
575 /* If the FS supports ACLs, we're here because we couldn't even open
576 the file for READ_CONTROL access. Chances are high that the file's
577 security descriptor has no ACE for "Everyone", so we should not fake
578 any access for "others". */
580 buf
->st_mode
&= ~(S_IROTH
| S_IWOTH
| S_IXOTH
);
584 syscall_printf ("0 = fstat (%S, %p) st_size=%D, st_mode=0%o, st_ino=%D"
585 "st_atim=%lx.%lx st_ctim=%lx.%lx "
586 "st_mtim=%lx.%lx st_birthtim=%lx.%lx",
587 pc
.get_nt_native_path (), buf
,
588 buf
->st_size
, buf
->st_mode
, buf
->st_ino
,
589 buf
->st_atim
.tv_sec
, buf
->st_atim
.tv_nsec
,
590 buf
->st_ctim
.tv_sec
, buf
->st_ctim
.tv_nsec
,
591 buf
->st_mtim
.tv_sec
, buf
->st_mtim
.tv_nsec
,
592 buf
->st_birthtim
.tv_sec
, buf
->st_birthtim
.tv_nsec
);
597 fhandler_disk_file::fstat (struct stat
*buf
)
599 return fstat_fs (buf
);
603 fhandler_disk_file::fstatvfs (struct statvfs
*sfs
)
605 int ret
= -1, opened
= 0;
607 /* We must not use the stat handle here, even if it exists. The handle
608 has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
609 mount point, it points to the FS of the mount point, rather than to the
611 HANDLE fh
= get_handle ();
615 OBJECT_ATTRIBUTES attr
;
616 opened
= NT_SUCCESS (NtOpenFile (&fh
, READ_CONTROL
,
617 pc
.get_object_attr (attr
, sec_none_nih
),
618 &io
, FILE_SHARE_VALID_FLAGS
,
619 FILE_OPEN_FOR_BACKUP_INTENT
));
622 /* Can't open file. Try again with parent dir. */
623 UNICODE_STRING dirname
;
624 RtlSplitUnicodePath (pc
.get_nt_native_path (), &dirname
, NULL
);
625 attr
.ObjectName
= &dirname
;
626 opened
= NT_SUCCESS (NtOpenFile (&fh
, READ_CONTROL
, &attr
, &io
,
627 FILE_SHARE_VALID_FLAGS
,
628 FILE_OPEN_FOR_BACKUP_INTENT
));
634 ret
= fstatvfs_by_handle (fh
, sfs
);
638 syscall_printf ("%d = fstatvfs(%s, %p)", ret
, get_name (), sfs
);
643 fhandler_base::fstatvfs_by_handle (HANDLE fh
, struct statvfs
*sfs
)
648 FILE_FS_FULL_SIZE_INFORMATION full_fsi
;
650 sfs
->f_files
= ULONG_MAX
;
651 sfs
->f_ffree
= ULONG_MAX
;
652 sfs
->f_favail
= ULONG_MAX
;
653 sfs
->f_fsid
= pc
.fs_serial_number ();
654 sfs
->f_flag
= pc
.fs_flags ();
655 sfs
->f_namemax
= pc
.fs_name_len ();
656 /* Get allocation related information. */
657 status
= NtQueryVolumeInformationFile (fh
, &io
, &full_fsi
, sizeof full_fsi
,
658 FileFsFullSizeInformation
);
659 if (NT_SUCCESS (status
))
661 sfs
->f_bsize
= full_fsi
.BytesPerSector
* full_fsi
.SectorsPerAllocationUnit
;
662 sfs
->f_frsize
= sfs
->f_bsize
;
663 sfs
->f_blocks
= (fsblkcnt_t
) full_fsi
.TotalAllocationUnits
.QuadPart
;
664 sfs
->f_bfree
= (fsblkcnt_t
)
665 full_fsi
.ActualAvailableAllocationUnits
.QuadPart
;
666 sfs
->f_bavail
= (fsblkcnt_t
)
667 full_fsi
.CallerAvailableAllocationUnits
.QuadPart
;
668 if (sfs
->f_bfree
> sfs
->f_bavail
)
670 /* Quotas active. We can't trust TotalAllocationUnits. */
671 NTFS_VOLUME_DATA_BUFFER nvdb
;
673 status
= NtFsControlFile (fh
, NULL
, NULL
, NULL
, &io
,
674 FSCTL_GET_NTFS_VOLUME_DATA
,
675 NULL
, 0, &nvdb
, sizeof nvdb
);
676 if (!NT_SUCCESS (status
))
677 debug_printf ("%y = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
678 status
, pc
.get_nt_native_path ());
680 sfs
->f_blocks
= (fsblkcnt_t
) nvdb
.TotalClusters
.QuadPart
;
684 else if (status
== STATUS_INVALID_PARAMETER
/* Netapp */
685 || status
== STATUS_INVALID_INFO_CLASS
)
687 FILE_FS_SIZE_INFORMATION fsi
;
688 status
= NtQueryVolumeInformationFile (fh
, &io
, &fsi
, sizeof fsi
,
689 FileFsSizeInformation
);
690 if (NT_SUCCESS (status
))
692 sfs
->f_bsize
= fsi
.BytesPerSector
* fsi
.SectorsPerAllocationUnit
;
693 sfs
->f_frsize
= sfs
->f_bsize
;
694 sfs
->f_blocks
= (fsblkcnt_t
) fsi
.TotalAllocationUnits
.QuadPart
;
695 sfs
->f_bfree
= sfs
->f_bavail
=
696 (fsblkcnt_t
) fsi
.AvailableAllocationUnits
.QuadPart
;
700 debug_printf ("%y = NtQueryVolumeInformationFile"
701 "(%S, FileFsSizeInformation)",
702 status
, pc
.get_nt_native_path ());
705 debug_printf ("%y = NtQueryVolumeInformationFile"
706 "(%S, FileFsFullSizeInformation)",
707 status
, pc
.get_nt_native_path ());
712 fhandler_disk_file::fchmod (mode_t mode
)
719 if (pc
.is_fs_special ()
720 /* For NFS, only handle Cygwin FIFOs specially. Changing mode of
721 native FIFOs will work with the default code below. */
722 && (!pc
.fs_is_nfs () || pc
.nfsattr ()->filler1
== NF3FIFO
))
723 return chmod_device (pc
, mode
);
727 query_open (query_write_dac
);
728 if (!(oret
= open (O_BINARY
, 0)))
730 /* Need WRITE_DAC to write ACLs. */
733 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
734 query_open (query_write_attributes
);
735 if (!(oret
= open (O_BINARY
, 0)))
742 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
743 Only type and mode have to be set. Apparently type isn't checked
744 for consistency, so it's sufficent to set it to NF3REG all the time. */
746 FILE_FULL_EA_INFORMATION ffei
;
747 char buf
[sizeof (NFS_V3_ATTR
) + sizeof (fattr3
)];
749 ffei_buf
.ffei
.NextEntryOffset
= 0;
750 ffei_buf
.ffei
.Flags
= 0;
751 ffei_buf
.ffei
.EaNameLength
= sizeof (NFS_V3_ATTR
) - 1;
752 ffei_buf
.ffei
.EaValueLength
= sizeof (fattr3
);
753 strcpy (ffei_buf
.ffei
.EaName
, NFS_V3_ATTR
);
754 fattr3
*nfs_attr
= (fattr3
*) (ffei_buf
.ffei
.EaName
755 + ffei_buf
.ffei
.EaNameLength
+ 1);
756 memset (nfs_attr
, 0, sizeof (fattr3
));
757 nfs_attr
->type
= NF3REG
;
758 nfs_attr
->mode
= mode
;
759 status
= NtSetEaFile (get_handle (), &io
,
760 &ffei_buf
.ffei
, sizeof ffei_buf
);
761 if (!NT_SUCCESS (status
))
762 __seterrno_from_nt_status (status
);
770 security_descriptor sd
, sd_ret
;
775 bool standard_acl
= false;
777 mode_t attr
= pc
.isdir () ? S_IFDIR
: 0;
779 if (!get_file_sd (get_handle (), pc
, sd
, false))
781 aclp
= (aclent_t
*) tp
.c_get ();
782 if ((nentries
= get_posix_access (sd
, attr
, &uid
, &gid
,
783 aclp
, MAX_ACL_ENTRIES
,
784 &standard_acl
)) >= 0)
786 /* Overwrite ACL permissions as required by POSIX 1003.1e
788 aclp
[0].a_perm
= (mode
>> 6) & S_IRWXO
;
790 /* POSIXly correct: If CLASS_OBJ is present, chmod only modifies
791 CLASS_OBJ, not GROUP_OBJ.
793 Deliberate deviation from POSIX 1003.1e: If the ACL is a
794 "standard" ACL, that is, it only contains POSIX permissions
795 as well as entries for the Administrators group and SYSTEM,
796 then it's kind of a POSIX-only ACL in a twisted, Windowsy
797 way. If so, we change GROUP_OBJ and CLASS_OBJ perms. */
799 && (idx
= searchace (aclp
, nentries
, GROUP_OBJ
)) >= 0)
800 aclp
[idx
].a_perm
= (mode
>> 3) & S_IRWXO
;
801 if (nentries
> MIN_ACL_ENTRIES
802 && (idx
= searchace (aclp
, nentries
, CLASS_OBJ
)) >= 0)
803 aclp
[idx
].a_perm
= (mode
>> 3) & S_IRWXO
;
805 if ((idx
= searchace (aclp
, nentries
, OTHER_OBJ
)) >= 0)
806 aclp
[idx
].a_perm
= mode
& S_IRWXO
;
809 if (set_posix_access (mode
, uid
, gid
, aclp
, nentries
, sd_ret
,
811 ret
= set_file_sd (get_handle (), pc
, sd_ret
, false);
816 /* If the mode has any write bits set, the DOS R/O flag is in the way. */
817 if (mode
& (S_IWUSR
| S_IWGRP
| S_IWOTH
))
818 pc
&= (DWORD
) ~FILE_ATTRIBUTE_READONLY
;
819 else if (!pc
.has_acls ()) /* Never set DOS R/O if security is used. */
820 pc
|= (DWORD
) FILE_ATTRIBUTE_READONLY
;
822 pc
|= (DWORD
) FILE_ATTRIBUTE_SYSTEM
;
824 status
= NtSetAttributesFile (get_handle (), pc
.file_attributes ());
825 /* MVFS needs a good amount of kicking to be convinced that it has to write
826 back metadata changes and to invalidate the cached metadata information
827 stored for the given handle. This method to open a second handle to
828 the file and write the same metadata information twice has been found
829 experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
830 if (pc
.fs_is_mvfs () && NT_SUCCESS (status
) && !oret
)
832 OBJECT_ATTRIBUTES attr
;
835 if (NT_SUCCESS (NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
,
836 pc
.init_reopen_attr (attr
, get_handle ()),
837 &io
, FILE_SHARE_VALID_FLAGS
,
838 FILE_OPEN_FOR_BACKUP_INTENT
)))
840 NtSetAttributesFile (fh
, pc
.file_attributes ());
844 /* Correct NTFS security attributes have higher priority */
847 if (!NT_SUCCESS (status
))
848 __seterrno_from_nt_status (status
);
861 fhandler_disk_file::fchown (uid_t uid
, gid_t gid
)
865 security_descriptor sd
, sd_ret
;
866 mode_t attr
= pc
.isdir () ? S_IFDIR
: 0;
875 /* fake - if not supported, pretend we're like win95
876 where it just works */
877 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
883 query_open (query_write_control
);
884 if (!(oret
= fhandler_disk_file::open (O_BINARY
, 0)))
888 if (get_file_sd (get_handle (), pc
, sd
, false))
891 aclp
= (aclent_t
*) tp
.c_get ();
892 if ((nentries
= get_posix_access (sd
, attr
, &old_uid
, &old_gid
,
893 aclp
, MAX_ACL_ENTRIES
)) < 0)
896 /* According to POSIX, chown can be a no-op if uid is (uid_t)-1 and
897 gid is (gid_t)-1. Otherwise, even if uid and gid are unchanged,
898 we must ensure that ctime is updated. */
899 if (uid
== ILLEGAL_UID
&& gid
== ILLEGAL_GID
)
904 if (uid
== ILLEGAL_UID
)
906 else if (gid
== ILLEGAL_GID
)
909 /* Windows ACLs can contain permissions for one group, while being owned by
910 another user/group. The permission bits returned above are pretty much
911 useless then. Creating a new ACL with these useless permissions results
912 in a potentially broken symlink. So what we do here is to set the
913 underlying permissions of symlinks to a sensible value which allows the
914 world to read the symlink and only the new owner to change it. */
916 for (int idx
= 0; idx
< nentries
; ++idx
)
918 aclp
[idx
].a_perm
|= S_IROTH
;
919 if (aclp
[idx
].a_type
& USER_OBJ
)
920 aclp
[idx
].a_perm
|= S_IWOTH
;
923 if (set_posix_access (attr
, uid
, gid
, aclp
, nentries
, sd_ret
,
925 ret
= set_file_sd (get_handle (), pc
, sd_ret
, true);
927 /* If you're running a Samba server with no winbind, the uid<->SID mapping
928 is disfunctional. Even trying to chown to your own account fails since
929 the account used on the server is the UNIX account which gets used for
930 the standard user mapping. This is a default mechanism which doesn't
931 know your real Windows SID. There are two possible error codes in
932 different Samba releases for this situation, one of them unfortunately
933 the not very significant STATUS_ACCESS_DENIED. Instead of relying on
934 the error codes, we're using the below very simple heuristic.
935 If set_file_sd failed, and the original user account was either already
936 unknown, or one of the standard UNIX accounts, we're faking success. */
937 if (ret
== -1 && pc
.fs_is_samba ())
942 || ((sid
= sidfromuid (old_uid
, NULL
)) != NO_SID
943 && RtlEqualPrefixSid (sid
,
944 well_known_samba_unix_user_fake_sid
)))
946 debug_printf ("Faking chown worked on standalone Samba");
959 fhandler_disk_file::facl (int cmd
, int nentries
, aclent_t
*aclbufp
)
971 /* Open for writing required to be able to set ctime
972 (even though setting the ACL is just pretended). */
974 oret
= open (O_WRONLY
| O_BINARY
, 0);
980 else if (nentries
< MIN_ACL_ENTRIES
)
987 aclbufp
[0].a_type
= USER_OBJ
;
988 aclbufp
[0].a_id
= st
.st_uid
;
989 aclbufp
[0].a_perm
= (st
.st_mode
& S_IRWXU
) >> 6;
990 aclbufp
[1].a_type
= GROUP_OBJ
;
991 aclbufp
[1].a_id
= st
.st_gid
;
992 aclbufp
[1].a_perm
= (st
.st_mode
& S_IRWXG
) >> 3;
993 aclbufp
[2].a_type
= OTHER_OBJ
;
994 aclbufp
[2].a_id
= ILLEGAL_GID
;
995 aclbufp
[2].a_perm
= st
.st_mode
& S_IRWXO
;
996 res
= MIN_ACL_ENTRIES
;
1001 res
= MIN_ACL_ENTRIES
;
1010 if ((cmd
== SETACL
&& !get_handle ())
1011 || (cmd
!= SETACL
&& !get_stat_handle ()))
1013 query_open (cmd
== SETACL
? query_write_dac
: query_read_control
);
1014 if (!(oret
= open (O_BINARY
, 0)))
1016 if (cmd
== GETACL
|| cmd
== GETACLCNT
)
1017 goto cant_access_acl
;
1024 if (!aclsort (nentries
, 0, aclbufp
))
1027 res
= setacl (get_handle (), pc
, nentries
, aclbufp
, rw
);
1031 FILE_BASIC_INFORMATION fbi
;
1032 fbi
.CreationTime
.QuadPart
1033 = fbi
.LastAccessTime
.QuadPart
1034 = fbi
.LastWriteTime
.QuadPart
1035 = fbi
.ChangeTime
.QuadPart
= 0LL;
1036 fbi
.FileAttributes
= (pc
.file_attributes ()
1037 & ~FILE_ATTRIBUTE_READONLY
)
1038 ?: FILE_ATTRIBUTE_NORMAL
;
1039 NtSetInformationFile (get_handle (), &io
, &fbi
, sizeof fbi
,
1040 FileBasicInformation
);
1048 res
= getacl (get_stat_handle (), pc
, nentries
, aclbufp
);
1049 /* For this ENOSYS case, see security.cc:get_file_attribute(). */
1050 if (res
== -1 && get_errno () == ENOSYS
)
1051 goto cant_access_acl
;
1055 res
= getacl (get_stat_handle (), pc
, 0, NULL
);
1057 if (res
== -1 && get_errno () == ENOSYS
)
1058 goto cant_access_acl
;
1073 fhandler_disk_file::fgetxattr (const char *name
, void *value
, size_t size
)
1075 if (pc
.is_fs_special ())
1077 set_errno (ENOTSUP
);
1080 return read_ea (get_handle (), pc
, name
, (char *) value
, size
);
1084 fhandler_disk_file::fsetxattr (const char *name
, const void *value
, size_t size
,
1087 if (pc
.is_fs_special ())
1089 set_errno (ENOTSUP
);
1092 return write_ea (get_handle (), pc
, name
, (const char *) value
, size
, flags
);
1096 fhandler_disk_file::fadvise (off_t offset
, off_t length
, int advice
)
1098 if (advice
< POSIX_FADV_NORMAL
|| advice
> POSIX_FADV_NOREUSE
)
1101 /* Windows only supports advice flags for the whole file. We're using
1102 a simplified test here so that we don't have to ask for the actual
1103 file size. Length == 0 means all bytes starting at offset anyway.
1104 So we only actually follow the advice, if it's given for offset == 0. */
1108 /* We only support normal and sequential mode for now. Everything which
1109 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1110 if (advice
!= POSIX_FADV_SEQUENTIAL
)
1111 advice
= POSIX_FADV_NORMAL
;
1114 FILE_MODE_INFORMATION fmi
;
1115 NTSTATUS status
= NtQueryInformationFile (get_handle (), &io
,
1117 FileModeInformation
);
1118 if (NT_SUCCESS (status
))
1120 fmi
.Mode
&= ~FILE_SEQUENTIAL_ONLY
;
1121 if (advice
== POSIX_FADV_SEQUENTIAL
)
1122 fmi
.Mode
|= FILE_SEQUENTIAL_ONLY
;
1123 status
= NtSetInformationFile (get_handle (), &io
, &fmi
, sizeof fmi
,
1124 FileModeInformation
);
1125 if (NT_SUCCESS (status
))
1127 __seterrno_from_nt_status (status
);
1130 return geterrno_from_nt_status (status
);
1134 fhandler_disk_file::falloc_allocate (int mode
, off_t offset
, off_t length
)
1138 FILE_STANDARD_INFORMATION fsi
;
1139 FILE_END_OF_FILE_INFORMATION feofi
;
1140 FILE_ALLOCATION_INFORMATION fai
= { 0 };
1143 status
= NtQueryInformationFile (get_handle (), &io
, &fsi
, sizeof fsi
,
1144 FileStandardInformation
);
1145 if (!NT_SUCCESS (status
))
1146 return geterrno_from_nt_status (status
);
1151 /* For posix_fallocate(3), truncating the file is a no-op. However,
1152 for sparse files we still have to allocate the blocks within
1153 offset and offset + length which are currently in holes, due to
1154 the following POSIX requirement:
1155 "If posix_fallocate() returns successfully, subsequent writes to
1156 the specified file data shall not fail due to the lack of free
1157 space on the file system storage media." */
1158 if (offset
+ length
<= fsi
.EndOfFile
.QuadPart
)
1160 if (!has_attribute (FILE_ATTRIBUTE_SPARSE_FILE
))
1162 feofi
.EndOfFile
.QuadPart
= fsi
.EndOfFile
.QuadPart
;
1165 feofi
.EndOfFile
.QuadPart
= offset
+ length
;
1167 case __FALLOC_FL_TRUNCATE
:
1168 /* For ftruncate(2), offset is 0. Just use length as is. */
1169 feofi
.EndOfFile
.QuadPart
= length
;
1171 /* Make file sparse only when called through ftruncate and the mount
1172 mode supports sparse files. Also, make sure that the new region
1173 actually spans over at least one sparsifiable chunk. */
1174 if (pc
.support_sparse ()
1175 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE
)
1176 && span_sparse_chunk (feofi
.EndOfFile
.QuadPart
,
1177 fsi
.EndOfFile
.QuadPart
))
1179 status
= NtFsControlFile (get_handle (), NULL
, NULL
, NULL
, &io
,
1180 FSCTL_SET_SPARSE
, NULL
, 0, NULL
, 0);
1181 if (NT_SUCCESS (status
))
1182 pc
.file_attributes (pc
.file_attributes ()
1183 | FILE_ATTRIBUTE_SPARSE_FILE
);
1184 debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1185 status
, pc
.get_nt_native_path ());
1188 case FALLOC_FL_KEEP_SIZE
:
1189 /* Keep track of the allocation size for overallocation below.
1190 Note that overallocation in Windows is only temporary!
1191 As soon as the last open handle to the file is closed, the
1192 overallocation gets removed by the system. Also, overallocation
1193 for sparse files fails silently, so just don't bother. */
1194 if (offset
+ length
> fsi
.EndOfFile
.QuadPart
1195 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE
))
1196 fai
.AllocationSize
.QuadPart
= offset
+ length
;
1198 feofi
.EndOfFile
.QuadPart
= fsi
.EndOfFile
.QuadPart
;
1202 /* Now set the new EOF */
1203 if (feofi
.EndOfFile
.QuadPart
!= fsi
.EndOfFile
.QuadPart
)
1205 status
= NtSetInformationFile (get_handle (), &io
,
1206 &feofi
, sizeof feofi
,
1207 FileEndOfFileInformation
);
1208 if (!NT_SUCCESS (status
))
1209 return geterrno_from_nt_status (status
);
1212 /* If called via fallocate(2) or posix_fallocate(3), allocate blocks in
1213 sparse file holes. */
1214 if (mode
!= __FALLOC_FL_TRUNCATE
1216 && has_attribute (FILE_ATTRIBUTE_SPARSE_FILE
))
1218 int res
= falloc_zero_range (mode
| __FALLOC_FL_ZERO_HOLES
,
1224 /* Last but not least, set the new allocation size, if any */
1225 if (fai
.AllocationSize
.QuadPart
)
1227 /* This is not fatal. Just note a failure in the debug output. */
1228 status
= NtSetInformationFile (get_handle (), &io
,
1230 FileAllocationInformation
);
1231 if (!NT_SUCCESS (status
))
1232 debug_printf ("%y = NtSetInformationFile(%S, "
1233 "FileAllocationInformation)",
1234 status
, pc
.get_nt_native_path ());
1241 fhandler_disk_file::falloc_punch_hole (off_t offset
, off_t length
)
1245 FILE_STANDARD_INFORMATION fsi
;
1246 FILE_ZERO_DATA_INFORMATION fzi
;
1249 status
= NtQueryInformationFile (get_handle (), &io
, &fsi
, sizeof fsi
,
1250 FileStandardInformation
);
1251 if (!NT_SUCCESS (status
))
1252 return geterrno_from_nt_status (status
);
1254 if (offset
> fsi
.EndOfFile
.QuadPart
) /* no-op */
1257 if (offset
+ length
> fsi
.EndOfFile
.QuadPart
)
1258 length
= fsi
.EndOfFile
.QuadPart
- offset
;
1260 /* If the file isn't sparse yet, make it so. */
1261 if (!has_attribute (FILE_ATTRIBUTE_SPARSE_FILE
))
1263 status
= NtFsControlFile (get_handle (), NULL
, NULL
, NULL
, &io
,
1264 FSCTL_SET_SPARSE
, NULL
, 0, NULL
, 0);
1265 debug_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1266 status
, pc
.get_nt_native_path ());
1267 if (!NT_SUCCESS (status
))
1268 return geterrno_from_nt_status (status
);
1269 pc
.file_attributes (pc
.file_attributes () | FILE_ATTRIBUTE_SPARSE_FILE
);
1272 /* Now punch a hole. For once, FSCTL_SET_ZERO_DATA does it exactly as per
1273 fallocate(FALLOC_FL_PUNCH_HOLE) specs. */
1274 fzi
.FileOffset
.QuadPart
= offset
;
1275 fzi
.BeyondFinalZero
.QuadPart
= offset
+ length
;
1276 status
= NtFsControlFile (get_handle (), NULL
, NULL
, NULL
, &io
,
1277 FSCTL_SET_ZERO_DATA
, &fzi
, sizeof fzi
, NULL
, 0);
1278 if (!NT_SUCCESS (status
))
1279 return geterrno_from_nt_status (status
);
1285 fhandler_disk_file::falloc_zero_range (int mode
, off_t offset
, off_t length
)
1289 FILE_STANDARD_INFORMATION fsi
;
1290 FILE_ALLOCATED_RANGE_BUFFER inp
, *out
= NULL
;
1291 OBJECT_ATTRIBUTES attr
;
1294 size_t data_chunk_count
= 0;
1297 status
= NtQueryInformationFile (get_handle (), &io
, &fsi
, sizeof fsi
,
1298 FileStandardInformation
);
1299 if (!NT_SUCCESS (status
))
1300 return geterrno_from_nt_status (status
);
1302 /* offset and length must not exceed EOF with FALLOC_FL_KEEP_SIZE */
1303 if (mode
& FALLOC_FL_KEEP_SIZE
)
1305 if (offset
> fsi
.EndOfFile
.QuadPart
) /* no-op */
1308 if (offset
+ length
> fsi
.EndOfFile
.QuadPart
)
1309 length
= fsi
.EndOfFile
.QuadPart
- offset
;
1312 /* If the file is sparse, fetch the data ranges within the file
1313 to be able to recognize holes. */
1314 if (has_attribute (FILE_ATTRIBUTE_SPARSE_FILE
))
1316 inp
.FileOffset
.QuadPart
= offset
;
1317 inp
.Length
.QuadPart
= length
;
1318 out
= (FILE_ALLOCATED_RANGE_BUFFER
*) tp
.t_get ();
1319 status
= NtFsControlFile (get_handle (), NULL
, NULL
, NULL
,
1320 &io
, FSCTL_QUERY_ALLOCATED_RANGES
,
1321 &inp
, sizeof inp
, out
, 2 * NT_MAX_PATH
);
1322 if (!NT_ERROR (status
))
1323 data_chunk_count
= io
.Information
/ sizeof *out
;
1326 /* Re-open the file and use this handle ever after, so as not to
1327 move the file pointer of the original file object. */
1328 status
= NtOpenFile (&zo_handle
, SYNCHRONIZE
| GENERIC_WRITE
,
1329 pc
.init_reopen_attr (attr
, get_handle ()), &io
,
1330 FILE_SHARE_VALID_FLAGS
, get_options ());
1331 if (!NT_SUCCESS (status
))
1332 return geterrno_from_nt_status (status
);
1334 /* FILE_SPARSE_GRANULARITY == 2 * NT_MAX_PATH ==> fits exactly */
1335 char *nullbuf
= tp
.t_get ();
1336 memset (nullbuf
, 0, FILE_SPARSE_GRANULARITY
);
1339 /* Split range into chunks of size FILE_SPARSE_GRANULARITY and handle
1340 them according to being data or hole */
1341 LARGE_INTEGER off
= { QuadPart
:offset
};
1342 size_t start_idx
= 0;
1346 bool in_data
= true;
1348 if (off
.QuadPart
% FILE_SPARSE_GRANULARITY
) /* First block */
1349 chunk_len
= roundup2 (off
.QuadPart
, FILE_SPARSE_GRANULARITY
) - off
.QuadPart
;
1351 chunk_len
= FILE_SPARSE_GRANULARITY
;
1352 if (chunk_len
> length
) /* First or last block */
1355 /* Check if the current chunk is within data or hole */
1356 if (has_attribute (FILE_ATTRIBUTE_SPARSE_FILE
)
1357 && off
.QuadPart
< fsi
.EndOfFile
.QuadPart
)
1360 for (size_t idx
= start_idx
; idx
< data_chunk_count
; ++idx
)
1361 if (off
.QuadPart
>= out
[idx
].FileOffset
.QuadPart
)
1363 /* Skip entries with lower start address next time. */
1365 if (off
.QuadPart
< out
[idx
].FileOffset
.QuadPart
1366 + out
[idx
].Length
.QuadPart
)
1374 /* Eventually, write zeros into the block. Completely zero out data
1375 blocks, just write a single zero to former holes in sparse files.
1376 If __FALLOC_FL_ZERO_HOLES has been specified, only write to holes. */
1377 if (!(mode
& __FALLOC_FL_ZERO_HOLES
) || !in_data
)
1379 status
= NtWriteFile (zo_handle
, NULL
, NULL
, NULL
, &io
, nullbuf
,
1380 in_data
? chunk_len
: 1, &off
, NULL
);
1381 if (!NT_SUCCESS (status
))
1383 res
= geterrno_from_nt_status (status
);
1388 off
.QuadPart
+= chunk_len
;
1389 length
-= chunk_len
;
1392 NtClose (zo_handle
);
1397 fhandler_disk_file::fallocate (int mode
, off_t offset
, off_t length
)
1399 if (length
< 0 || !get_handle ())
1403 if (!(get_access () & GENERIC_WRITE
))
1409 case __FALLOC_FL_TRUNCATE
:
1410 case FALLOC_FL_KEEP_SIZE
:
1411 return falloc_allocate (mode
, offset
, length
);
1412 case FALLOC_FL_PUNCH_HOLE
| FALLOC_FL_KEEP_SIZE
:
1413 /* Only if the filesystem supports it... */
1414 if (!(pc
.fs_flags () & FILE_SUPPORTS_SPARSE_FILES
))
1416 return falloc_punch_hole (offset
, length
);
1417 case FALLOC_FL_ZERO_RANGE
:
1418 case FALLOC_FL_ZERO_RANGE
| FALLOC_FL_KEEP_SIZE
:
1419 return falloc_zero_range (mode
, offset
, length
);
1427 fhandler_disk_file::link (const char *newpath
)
1429 size_t nlen
= strlen (newpath
);
1430 path_conv
newpc (newpath
, PC_SYM_NOFOLLOW
| PC_POSIX
| PC_NULLEMPTY
, stat_suffixes
);
1433 set_errno (newpc
.error
);
1437 if (newpc
.exists ())
1439 syscall_printf ("file '%S' exists?", newpc
.get_nt_native_path ());
1444 if (isdirsep (newpath
[nlen
- 1]) || has_dot_last_component (newpath
, false))
1450 char new_buf
[nlen
+ 5];
1453 /* If the original file is a lnk special file,
1454 and if the original file has a .lnk suffix, add one to the hardlink
1456 if (pc
.is_lnk_special ()
1457 && RtlEqualUnicodePathSuffix (pc
.get_nt_native_path (),
1460 /* Shortcut hack. */
1461 stpcpy (stpcpy (new_buf
, newpath
), ".lnk");
1463 newpc
.check (newpath
, PC_SYM_NOFOLLOW
);
1465 else if (!pc
.isdir ()
1467 && RtlEqualUnicodePathSuffix (pc
.get_nt_native_path (),
1469 && !RtlEqualUnicodePathSuffix (newpc
.get_nt_native_path (),
1472 /* Executable hack. */
1473 stpcpy (stpcpy (new_buf
, newpath
), ".exe");
1475 newpc
.check (newpath
, PC_SYM_NOFOLLOW
);
1479 /* We only need READ_CONTROL access so the handle returned in pc is
1480 sufficient. And if the file couldn't be opened with READ_CONTROL
1481 access in path_conv, we won't be able to do it here anyway. */
1482 HANDLE fh
= get_stat_handle ();
1488 PUNICODE_STRING tgt
= newpc
.get_nt_native_path ();
1489 ULONG size
= sizeof (FILE_LINK_INFORMATION
) + tgt
->Length
;
1490 PFILE_LINK_INFORMATION pfli
= (PFILE_LINK_INFORMATION
) alloca (size
);
1491 pfli
->ReplaceIfExists
= FALSE
;
1492 pfli
->RootDirectory
= NULL
;
1493 memcpy (pfli
->FileName
, tgt
->Buffer
, pfli
->FileNameLength
= tgt
->Length
);
1497 status
= NtSetInformationFile (fh
, &io
, pfli
, size
, FileLinkInformation
);
1498 if (!NT_SUCCESS (status
))
1500 if (status
== STATUS_INVALID_DEVICE_REQUEST
1501 || status
== STATUS_NOT_SUPPORTED
)
1502 /* FS doesn't support hard links. Linux returns EPERM. */
1505 __seterrno_from_nt_status (status
);
1508 else if ((pc
.file_attributes () & O_TMPFILE_FILE_ATTRS
)
1509 == O_TMPFILE_FILE_ATTRS
)
1511 /* An O_TMPFILE file has FILE_ATTRIBUTE_TEMPORARY and
1512 FILE_ATTRIBUTE_HIDDEN set. After a successful hardlink the file is
1513 not temporary anymore in the usual sense. So we remove these
1516 Note that we don't create a reopen attribute for the original
1517 link but rather a normal attribute for the just created link.
1518 The reason is a curious behaviour of Windows: If we remove the
1519 O_TMPFILE attributes on the original link, the new link will not
1520 show up in file system listings (not even native ones from , e.g.,
1521 `cmd /c dir'), long after the original link has been closed and
1522 removed. The file and its metadata will be kept in memory only
1523 as long as Windows sees fit. By opening the new link, we request
1524 the attribute changes on the new link, so after closing it Windows
1525 will have an increased interest to write back the metadata. */
1526 OBJECT_ATTRIBUTES attr
;
1527 status
= NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
,
1528 newpc
.get_object_attr (attr
, sec_none_nih
), &io
,
1529 FILE_SHARE_VALID_FLAGS
, FILE_OPEN_FOR_BACKUP_INTENT
);
1530 if (!NT_SUCCESS (status
))
1531 debug_printf ("Opening for removing TEMPORARY attrib failed, "
1532 "status = %y", status
);
1535 FILE_BASIC_INFORMATION fbi
;
1537 fbi
.CreationTime
.QuadPart
= fbi
.LastAccessTime
.QuadPart
1538 = fbi
.LastWriteTime
.QuadPart
= fbi
.ChangeTime
.QuadPart
= 0LL;
1539 fbi
.FileAttributes
= (pc
.file_attributes () & ~O_TMPFILE_FILE_ATTRS
)
1540 ?: FILE_ATTRIBUTE_NORMAL
;
1541 status
= NtSetInformationFile (fh
, &io
, &fbi
, sizeof fbi
,
1542 FileBasicInformation
);
1543 if (!NT_SUCCESS (status
))
1544 debug_printf ("Removing the TEMPORARY attrib failed, status = %y",
1553 fhandler_disk_file::utimens (const struct timespec
*tvp
)
1555 return utimens_fs (tvp
);
1559 fhandler_base::utimens_fs (const struct timespec
*tvp
)
1561 struct timespec timeofday
;
1562 struct timespec tmp
[2];
1563 bool closeit
= false;
1567 query_open (query_write_attributes
);
1568 if (!open_fs (O_BINARY
, 0))
1570 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1571 to change the timestamps. Unfortunately it's not sufficient for a
1572 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1573 for writing, though this fails for R/O files of course. */
1574 query_open (no_query
);
1575 if (!open_fs (O_WRONLY
| O_BINARY
, 0))
1577 syscall_printf ("Opening file failed");
1584 clock_gettime (CLOCK_REALTIME
, &timeofday
);
1586 tmp
[1] = tmp
[0] = timeofday
;
1589 if ((tvp
[0].tv_nsec
< UTIME_NOW
|| tvp
[0].tv_nsec
>= NSPERSEC
)
1590 || (tvp
[1].tv_nsec
< UTIME_NOW
|| tvp
[1].tv_nsec
>= NSPERSEC
))
1597 tmp
[0] = (tvp
[0].tv_nsec
== UTIME_NOW
) ? timeofday
: tvp
[0];
1598 tmp
[1] = (tvp
[1].tv_nsec
== UTIME_NOW
) ? timeofday
: tvp
[1];
1600 debug_printf ("incoming lastaccess %ly %ly", tmp
[0].tv_sec
, tmp
[0].tv_nsec
);
1603 FILE_BASIC_INFORMATION fbi
;
1605 fbi
.CreationTime
.QuadPart
= 0LL;
1606 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1607 timespec_to_filetime (&tmp
[0], &fbi
.LastAccessTime
);
1608 timespec_to_filetime (&tmp
[1], &fbi
.LastWriteTime
);
1609 fbi
.ChangeTime
.QuadPart
= 0LL;
1610 fbi
.FileAttributes
= 0;
1611 NTSTATUS status
= NtSetInformationFile (get_handle (), &io
, &fbi
, sizeof fbi
,
1612 FileBasicInformation
);
1613 /* For this special case for MVFS see the comment in
1614 fhandler_disk_file::fchmod. */
1615 if (pc
.fs_is_mvfs () && NT_SUCCESS (status
) && !closeit
)
1617 OBJECT_ATTRIBUTES attr
;
1620 if (NT_SUCCESS (NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
,
1621 pc
.init_reopen_attr (attr
, get_handle ()),
1622 &io
, FILE_SHARE_VALID_FLAGS
,
1623 FILE_OPEN_FOR_BACKUP_INTENT
)))
1625 NtSetInformationFile (fh
, &io
, &fbi
, sizeof fbi
,
1626 FileBasicInformation
);
1632 /* Opening a directory on a 9x share from a NT machine works(!), but
1633 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1634 if (!NT_SUCCESS (status
) && status
!= STATUS_NOT_SUPPORTED
)
1636 __seterrno_from_nt_status (status
);
1642 fhandler_disk_file::fhandler_disk_file () :
1643 fhandler_base (), prw_handle (NULL
)
1647 fhandler_disk_file::fhandler_disk_file (path_conv
&pc
) :
1648 fhandler_base (), prw_handle (NULL
)
1654 fhandler_disk_file::open (int flags
, mode_t mode
)
1656 return open_fs (flags
, mode
);
1660 fhandler_disk_file::close ()
1662 /* Close extra pread/pwrite handle, if it exists. */
1664 NtClose (prw_handle
);
1665 return fhandler_base::close ();
1669 fhandler_disk_file::fcntl (int cmd
, intptr_t arg
)
1675 case F_LCK_MANDATORY
: /* Mandatory locking only works on files. */
1676 mandatory_locking (!!arg
);
1677 need_fork_fixup (true);
1681 res
= fhandler_base::fcntl (cmd
, arg
);
1688 fhandler_disk_file::dup (fhandler_base
*child
, int flags
)
1690 fhandler_disk_file
*fhc
= (fhandler_disk_file
*) child
;
1692 int ret
= fhandler_base::dup (child
, flags
);
1693 if (!ret
&& prw_handle
1694 && !DuplicateHandle (GetCurrentProcess (), prw_handle
,
1695 GetCurrentProcess (), &fhc
->prw_handle
,
1696 0, FALSE
, DUPLICATE_SAME_ACCESS
))
1697 fhc
->prw_handle
= NULL
;
1702 fhandler_disk_file::fixup_after_fork (HANDLE parent
)
1705 mandatory_locking (false);
1706 fhandler_base::fixup_after_fork (parent
);
1710 fhandler_base::open_fs (int flags
, mode_t mode
)
1712 /* Unfortunately NT allows to open directories for writing, but that's
1713 disallowed according to SUSv3. */
1714 if (pc
.isdir () && (flags
& O_ACCMODE
) != O_RDONLY
)
1720 bool new_file
= !exists ();
1722 int res
= fhandler_base::open (flags
, mode
);
1725 /* The file info in pc is wrong at this point for newly created files.
1726 Refresh it before fetching any file info. */
1728 pc
.get_finfo (get_handle ());
1730 if (pc
.isgood_inode (pc
.get_ino ()))
1731 ino
= pc
.get_ino ();
1734 syscall_printf ("%d = fhandler_disk_file::open(%S, %y)", res
,
1735 pc
.get_nt_native_path (), flags
);
1739 /* POSIX demands that pread/pwrite don't change the current file position.
1740 While NtReadFile/NtWriteFile support atomic seek-and-io, both change the
1741 file pointer if the file handle has been opened for synchonous I/O.
1742 Using this handle for pread/pwrite would break atomicity, because the
1743 read/write operation would have to be followed by a seek back to the old
1744 file position. What we do is to open another handle to the file on the
1745 first call to either pread or pwrite. This is used for any subsequent
1746 pread/pwrite. Thus the current file position of the "normal" file
1747 handle is not touched.
1751 Note that this is just a hack. The problem with this approach is that
1752 a change to the file permissions might disallow to open the file with
1753 the required permissions to read or write. This appears to be a border case,
1754 but that's exactly what git does. It creates the file for reading and
1755 writing and after writing it, it chmods the file to read-only. Then it
1756 calls pread on the file to examine the content. This works, but if git
1757 would use the original handle to pwrite to the file, it would be broken
1760 One way to implement this is to open the pread/pwrite handle right at
1761 file open time. We would simply maintain two handles, which wouldn't
1762 be much of a problem given how we do that for other fhandler types as
1765 However, ultimately fhandler_disk_file should become a derived class of
1766 fhandler_base_overlapped. Each raw_read or raw_write would fetch the
1767 actual file position, read/write from there, and then set the file
1768 position again. Fortunately, while the file position is not maintained
1769 by the I/O manager, it can be fetched and set to a new value by all
1770 processes holding a handle to that file object. Pread and pwrite differ
1771 from raw_read and raw_write just by not touching the current file pos.
1772 Actually they could be merged with raw_read/raw_write if we add a position
1773 parameter to the latter. */
1776 fhandler_disk_file::prw_open (bool write
, void *aio
)
1780 OBJECT_ATTRIBUTES attr
;
1781 ULONG options
= get_options ();
1783 /* If async i/o is intended, turn off the default synchronous operation */
1785 options
&= ~FILE_SYNCHRONOUS_IO_NONALERT
;
1787 /* First try to open with the original access mask */
1788 ACCESS_MASK access
= get_access ();
1789 status
= NtOpenFile (&prw_handle
, access
,
1790 pc
.init_reopen_attr (attr
, get_handle ()), &io
,
1791 FILE_SHARE_VALID_FLAGS
, options
);
1792 if (status
== STATUS_ACCESS_DENIED
)
1794 /* If we get an access denied, chmod has been called. Try again
1795 with just the required rights to perform the called function. */
1796 access
&= write
? ~GENERIC_READ
: ~GENERIC_WRITE
;
1797 status
= NtOpenFile (&prw_handle
, access
, &attr
, &io
,
1798 FILE_SHARE_VALID_FLAGS
, options
);
1800 debug_printf ("%y = NtOpenFile (%p, %y, %S, io, %y, %y)",
1801 status
, prw_handle
, access
, pc
.get_nt_native_path (),
1802 FILE_SHARE_VALID_FLAGS
, options
);
1803 if (!NT_SUCCESS (status
))
1805 __seterrno_from_nt_status (status
);
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
)
2160 set_errno (ENOTDIR
);
2161 else if ((dir
= (DIR *) malloc (sizeof (DIR))) == NULL
)
2163 else if ((dir
->__d_dirname
= (char *) malloc ( sizeof (struct __DIR_cache
)))
2169 else if ((dir
->__d_dirent
=
2170 (struct dirent
*) malloc (sizeof (struct dirent
))) == NULL
)
2178 if (cfd
< 0 && fd
< 0)
2181 dir
->__d_dirent
->__d_version
= __DIRENT_VERSION
;
2182 dir
->__d_cookie
= __DIRENT_COOKIE
;
2183 dir
->__handle
= INVALID_HANDLE_VALUE
;
2184 dir
->__d_position
= 0;
2185 dir
->__flags
= (get_name ()[0] == '/' && get_name ()[1] == '\0')
2186 ? dirent_isroot
: 0;
2187 dir
->__d_internal
= 0;
2189 if (pc
.iscygdrive ())
2191 if (fd
< 0 && !open (O_RDONLY
, 0))
2196 dir
->__d_internal
= (uintptr_t) new __DIR_mounts (get_name ());
2197 d_cachepos (dir
) = 0;
2200 /* opendir() case. Initialize with given directory name and
2201 NULL directory handle. */
2202 OBJECT_ATTRIBUTES attr
;
2205 /* Tools like ls(1) call dirfd() to fetch the directory
2206 descriptor for calls to facl or fstat. The tight access mask
2207 used so far is not sufficient to reuse the handle for these
2208 calls, instead the facl/fstat calls find the handle to be
2209 unusable and have to re-open the file for reading attributes
2210 and control data. So, what we do here is to try to open the
2211 directory with more relaxed access mask which enables to use
2212 the handle for the aforementioned purpose. This should work
2213 in almost all cases. Only if it doesn't work due to
2214 permission problems, we drop the additional access bits and
2216 ACCESS_MASK fstat_mask
= READ_CONTROL
| FILE_READ_ATTRIBUTES
;
2220 status
= NtOpenFile (&get_handle (),
2221 SYNCHRONIZE
| FILE_LIST_DIRECTORY
2223 pc
.get_object_attr (attr
, sec_none_nih
),
2224 &io
, FILE_SHARE_VALID_FLAGS
,
2225 FILE_SYNCHRONOUS_IO_NONALERT
2226 | FILE_OPEN_FOR_BACKUP_INTENT
2227 | FILE_DIRECTORY_FILE
);
2228 if (!NT_SUCCESS (status
))
2230 if (status
== STATUS_ACCESS_DENIED
&& fstat_mask
)
2234 __seterrno_from_nt_status (status
);
2239 while (!NT_SUCCESS (status
));
2242 /* FileIdBothDirectoryInformation was unsupported on XP when
2243 accessing UDF. It's not clear if the call isn't also unsupported
2244 on other OS/FS combinations. Instead of testing for yet another
2245 error code, use FileIdBothDirectoryInformation only on FSes
2246 supporting persistent ACLs.
2248 NFS clients hide dangling symlinks from directory queries,
2249 unless you use the FileNamesInformation info class.
2250 FileIdBothDirectoryInformation works fine, but only if the NFS
2251 share is mounted to a drive letter. TODO: We don't test that
2252 here for now, but it might be worth to test if there's a speed
2253 gain in using FileIdBothDirectoryInformation, because it doesn't
2254 require to open the file to read the inode number. */
2255 if (pc
.hasgood_inode ())
2257 dir
->__flags
|= dirent_set_d_ino
;
2258 if (pc
.fs_is_nfs ())
2259 dir
->__flags
|= dirent_nfs_d_ino
;
2260 else if (!pc
.has_buggy_fileid_dirinfo ())
2261 dir
->__flags
|= dirent_get_d_ino
;
2268 /* Filling cfd with `this' (aka storing this in the file
2269 descriptor table should only happen after it's clear that
2270 opendir doesn't fail, otherwise we end up cfree'ing the
2271 fhandler twice, once in opendir() in dir.cc, the second
2272 time on exit. Nasty, nasty... */
2276 set_close_on_exec (true);
2281 syscall_printf ("%p = opendir (%s)", res
, get_name ());
2285 delete d_mounts (dir
);
2287 free (dir
->__d_dirent
);
2289 free (dir
->__d_dirname
);
2296 readdir_get_ino (const char *path
, bool dot_dot
)
2301 OBJECT_ATTRIBUTES attr
;
2307 fname
= (char *) alloca (strlen (path
) + 4);
2308 char *c
= stpcpy (fname
, path
);
2314 path_conv
pc (path
, PC_SYM_NOFOLLOW
| PC_POSIX
| PC_KEEP_HANDLE
);
2315 if (pc
.isspecial ())
2317 if (!stat_worker (pc
, &st
))
2320 else if (!pc
.hasgood_inode ())
2321 ino
= hash_path_name (0, pc
.get_nt_native_path ());
2322 else if ((hdl
= pc
.handle ()) != NULL
2323 || NT_SUCCESS (NtOpenFile (&hdl
, READ_CONTROL
,
2324 pc
.get_object_attr (attr
, sec_none_nih
),
2325 &io
, FILE_SHARE_VALID_FLAGS
,
2326 FILE_OPEN_FOR_BACKUP_INTENT
2327 | (pc
.is_known_reparse_point ()
2328 ? FILE_OPEN_REPARSE_POINT
: 0)))
2331 ino
= pc
.get_ino_by_handle (hdl
);
2333 ino
= hash_path_name (0, pc
.get_nt_native_path ());
2339 fhandler_disk_file::readdir_helper (DIR *dir
, dirent
*de
, DWORD w32_err
,
2340 DWORD attr
, PUNICODE_STRING fname
)
2344 switch (d_mounts (dir
)->check_missing_mount (fname
))
2346 case __DIR_mount_none
:
2348 return geterrno_from_win_error (w32_err
);
2349 case __DIR_mount_virt_target
:
2350 de
->d_type
= DT_DIR
;
2356 dir
->__flags
&= ~dirent_set_d_ino
;
2359 /* Set d_type if type can be determined from file attributes. For .lnk
2360 symlinks, d_type will be reset below. Reparse points can be NTFS
2361 symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
2362 if (attr
&& !(attr
& ~FILE_ATTRIBUTE_VALID_FLAGS
))
2364 if (attr
& FILE_ATTRIBUTE_DIRECTORY
)
2365 de
->d_type
= DT_DIR
;
2366 /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
2367 else if (!(attr
& FILE_ATTRIBUTE_SYSTEM
))
2368 de
->d_type
= DT_REG
;
2371 /* Check for reparse points that can be treated as posix symlinks.
2372 Mountpoints and unknown or unhandled reparse points will be treated
2373 as normal file/directory/unknown. In all cases, returning the INO of
2374 the reparse point (not of the target) matches behavior of posix systems.
2376 if (attr
& FILE_ATTRIBUTE_REPARSE_POINT
)
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 for remote drives
2467 on NT4 and 2K systems. There are also hacked versions of
2468 Samba 3.0.x out there (Debian-based it seems), which return
2469 STATUS_NOT_SUPPORTED rather than handling this info class.
2470 We just fall back to using a standard directory query in
2471 this case and note this case using the dirent_get_d_ino flag. */
2472 if (!NT_SUCCESS (status
) && status
!= STATUS_NO_MORE_FILES
2473 && (status
== STATUS_INVALID_LEVEL
2474 || status
== STATUS_NOT_SUPPORTED
2475 || status
== STATUS_INVALID_PARAMETER
2476 || status
== STATUS_INVALID_NETWORK_RESPONSE
2477 || status
== STATUS_INVALID_INFO_CLASS
))
2478 dir
->__flags
&= ~dirent_get_d_ino
;
2479 /* Something weird happens on Samba up to version 3.0.21c, which is
2480 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
2481 nicely, but only up to the 128th entry in the directory. After
2482 reaching this entry, the next call to NtQueryDirectoryFile
2483 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
2484 Why should we care, we can just switch to
2485 FileBothDirectoryInformation, isn't it? Nope! The next call to
2486 NtQueryDirectoryFile(FileBothDirectoryInformation) actually
2487 returns STATUS_NO_MORE_FILES, regardless how many files are left
2488 unread in the directory. This does not happen when using
2489 FileBothDirectoryInformation right from the start, but since
2490 we can't decide whether the server we're talking with has this
2491 bug or not, we end up serving Samba shares always in the slow
2492 mode using FileBothDirectoryInformation. So, what we do here is
2493 to implement the solution suggested by Andrew Tridgell, we just
2494 reread all entries up to dir->d_position using
2495 FileBothDirectoryInformation.
2496 However, We do *not* mark this server as broken and fall back to
2497 using FileBothDirectoryInformation further on. This would slow
2498 down every access to such a server, even for directories under
2499 128 entries. Also, bigger dirs only suffer from one additional
2500 call per full directory scan, which shouldn't be too big a hit.
2501 This can easily be changed if necessary. */
2502 if (status
== STATUS_INVALID_LEVEL
&& dir
->__d_position
)
2504 d_cachepos (dir
) = 0;
2505 for (int cnt
= 0; cnt
< dir
->__d_position
; ++cnt
)
2507 if (d_cachepos (dir
) == 0)
2509 status
= NtQueryDirectoryFile (get_handle (), NULL
, NULL
,
2510 NULL
, &io
, d_cache (dir
),
2512 FileBothDirectoryInformation
,
2513 FALSE
, NULL
, cnt
== 0);
2514 if (!NT_SUCCESS (status
))
2517 buf
= (PFILE_ID_BOTH_DIR_INFORMATION
) (d_cache (dir
)
2518 + d_cachepos (dir
));
2519 if (buf
->NextEntryOffset
== 0)
2520 d_cachepos (dir
) = 0;
2522 d_cachepos (dir
) += buf
->NextEntryOffset
;
2527 /* NFS must use FileNamesInformation! Any other information class
2528 skips all symlinks. */
2529 if (!(dir
->__flags
& dirent_get_d_ino
))
2530 status
= NtQueryDirectoryFile (get_handle (), NULL
, NULL
, NULL
, &io
,
2531 d_cache (dir
), DIR_BUF_SIZE
,
2532 (dir
->__flags
& dirent_nfs_d_ino
)
2533 ? FileNamesInformation
2534 : FileBothDirectoryInformation
,
2535 FALSE
, NULL
, dir
->__d_position
== 0);
2540 if (status
== STATUS_NO_MORE_FILES
)
2542 else if (!NT_SUCCESS (status
))
2543 debug_printf ("NtQueryDirectoryFile failed, status %y, win32 error %u",
2544 status
, RtlNtStatusToDosError (status
));
2547 buf
= (PFILE_ID_BOTH_DIR_INFORMATION
) (d_cache (dir
) + d_cachepos (dir
));
2548 if (buf
->NextEntryOffset
== 0)
2549 d_cachepos (dir
) = 0;
2551 d_cachepos (dir
) += buf
->NextEntryOffset
;
2552 if ((dir
->__flags
& dirent_get_d_ino
))
2554 FileName
= buf
->FileName
;
2555 FileNameLength
= buf
->FileNameLength
;
2556 FileAttributes
= buf
->FileAttributes
;
2557 if ((dir
->__flags
& dirent_set_d_ino
))
2558 de
->d_ino
= buf
->FileId
.QuadPart
;
2560 else if ((dir
->__flags
& dirent_nfs_d_ino
))
2562 FileName
= ((PFILE_NAMES_INFORMATION
) buf
)->FileName
;
2563 FileNameLength
= ((PFILE_NAMES_INFORMATION
) buf
)->FileNameLength
;
2567 FileName
= ((PFILE_BOTH_DIR_INFORMATION
) buf
)->FileName
;
2569 ((PFILE_BOTH_DIR_INFORMATION
) buf
)->FileNameLength
;
2571 ((PFILE_BOTH_DIR_INFORMATION
) buf
)->FileAttributes
;
2573 RtlInitCountedUnicodeString (&fname
, FileName
, FileNameLength
);
2574 d_mounts (dir
)->check_mount (&fname
);
2575 if (de
->d_ino
== 0 && (dir
->__flags
& dirent_set_d_ino
))
2577 /* Don't try to optimize relative to dir->__d_position. On several
2578 filesystems it's no safe bet that "." and ".." entries always
2580 if (FileNameLength
== sizeof (WCHAR
) && FileName
[0] == '.')
2581 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2582 else if (FileNameLength
== 2 * sizeof (WCHAR
)
2583 && FileName
[0] == L
'.' && FileName
[1] == L
'.')
2585 if (!(dir
->__flags
& dirent_isroot
))
2586 de
->d_ino
= readdir_get_ino (get_name (), true);
2588 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2592 OBJECT_ATTRIBUTES attr
;
2596 InitializeObjectAttributes (&attr
, &fname
,
2597 pc
.objcaseinsensitive (),
2598 get_handle (), NULL
);
2599 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2600 NtOpenFile here returns the inode number of the symlink target,
2601 rather than the inode number of the symlink itself.
2603 Worse, trying to open a symlink without setting the special
2604 "ActOnSymlink" EA triggers a bug in Windows 7 which results
2605 in a timeout of up to 20 seconds, followed by two exceptions
2608 Since both results are far from desirable, we open symlinks
2609 on NFS so that we get the right inode and a happy W7.
2610 And, since some filesystems choke on the EAs, we don't
2611 use them unconditionally. */
2612 f_status
= (dir
->__flags
& dirent_nfs_d_ino
)
2613 ? NtCreateFile (&hdl
,
2614 READ_CONTROL
| FILE_READ_ATTRIBUTES
,
2615 &attr
, &io
, NULL
, 0,
2616 FILE_SHARE_VALID_FLAGS
, FILE_OPEN
,
2617 FILE_OPEN_FOR_BACKUP_INTENT
,
2618 &nfs_aol_ffei
, sizeof nfs_aol_ffei
)
2619 : NtOpenFile (&hdl
, READ_CONTROL
, &attr
, &io
,
2620 FILE_SHARE_VALID_FLAGS
,
2621 FILE_OPEN_FOR_BACKUP_INTENT
2622 | FILE_OPEN_REPARSE_POINT
);
2623 if (NT_SUCCESS (f_status
))
2625 /* We call NtQueryInformationFile here, rather than
2626 pc.get_ino_by_handle(), otherwise we can't short-circuit
2627 dirent_set_d_ino correctly. */
2628 FILE_INTERNAL_INFORMATION fii
;
2629 f_status
= NtQueryInformationFile (hdl
, &io
, &fii
, sizeof fii
,
2630 FileInternalInformation
);
2631 /* On NFS fetch the (faked, but useful) DOS attribute.
2632 We need it to recognize shortcut FIFOs. */
2633 if ((dir
->__flags
& dirent_nfs_d_ino
))
2635 FILE_BASIC_INFORMATION fbi
;
2637 if (NT_SUCCESS (NtQueryInformationFile (hdl
, &io
, &fbi
,
2638 sizeof fbi
, FileBasicInformation
)))
2639 FileAttributes
= fbi
.FileAttributes
;
2642 if (NT_SUCCESS (f_status
))
2644 if (pc
.isgood_inode (fii
.IndexNumber
.QuadPart
))
2645 de
->d_ino
= fii
.IndexNumber
.QuadPart
;
2647 /* Untrusted file system. Don't try to fetch inode
2649 dir
->__flags
&= ~dirent_set_d_ino
;
2656 if (!(res
= readdir_helper (dir
, de
, RtlNtStatusToDosError (status
),
2657 FileAttributes
, &fname
)))
2658 dir
->__d_position
++;
2659 else if (!(dir
->__flags
& dirent_saw_dot
))
2661 strcpy (de
->d_name
, ".");
2662 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2663 de
->d_type
= DT_DIR
;
2664 dir
->__d_position
++;
2665 dir
->__flags
|= dirent_saw_dot
;
2668 else if (!(dir
->__flags
& dirent_saw_dot_dot
))
2670 strcpy (de
->d_name
, "..");
2671 if (!(dir
->__flags
& dirent_isroot
))
2672 de
->d_ino
= readdir_get_ino (get_name (), true);
2674 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2675 de
->d_type
= DT_DIR
;
2676 dir
->__d_position
++;
2677 dir
->__flags
|= dirent_saw_dot_dot
;
2681 syscall_printf ("%d = readdir(%p, %p) (L\"%lS\" > \"%ls\") (attr %y > type %d)",
2682 res
, dir
, &de
, res
? NULL
: &fname
, res
? "***" : de
->d_name
,
2683 FileAttributes
, de
->d_type
);
2688 fhandler_disk_file::telldir (DIR *dir
)
2690 return dir
->__d_position
;
2694 fhandler_disk_file::seekdir (DIR *dir
, long loc
)
2697 while (loc
> dir
->__d_position
)
2698 if (!::readdir (dir
))
2703 fhandler_disk_file::rewinddir (DIR *dir
)
2705 d_cachepos (dir
) = 0;
2706 dir
->__d_position
= 0;
2707 d_mounts (dir
)->rewind ();
2711 fhandler_disk_file::closedir (DIR *dir
)
2715 delete d_mounts (dir
);
2716 syscall_printf ("%d = closedir(%p, %s)", res
, dir
, get_name ());
2721 fhandler_disk_file::fs_ioc_getflags ()
2725 FILE_BASIC_INFORMATION fbi
;
2726 FILE_CASE_SENSITIVE_INFORMATION fcsi
;
2729 status
= NtQueryInformationFile (get_handle (), &io
, &fbi
, sizeof fbi
,
2730 FileBasicInformation
);
2731 if (NT_SUCCESS (status
))
2733 flags
= (uint64_t) fbi
.FileAttributes
& FS_FL_USER_VISIBLE
;
2734 pc
.file_attributes (fbi
.FileAttributes
);
2737 flags
= (uint64_t) pc
.file_attributes () & FS_FL_USER_VISIBLE
;
2738 if (pc
.isdir () && wincap
.has_case_sensitive_dirs ()
2739 && !pc
.isremote () && pc
.fs_is_ntfs ())
2742 status
= NtQueryInformationFile (get_handle (), &io
,
2744 FileCaseSensitiveInformation
);
2745 if (NT_SUCCESS (status
)
2746 && (fcsi
.Flags
& FILE_CS_FLAG_CASE_SENSITIVE_DIR
))
2747 flags
|= FS_CASESENS_FL
;
2752 /* Settable DOS attributes */
2753 #define FS_FL_SETATTRIBS (FS_READONLY_FL \
2761 fhandler_disk_file::fs_ioc_setflags (uint64_t flags
)
2767 OBJECT_ATTRIBUTES attr
;
2769 FILE_BASIC_INFORMATION fbi
;
2770 FILE_SET_SPARSE_BUFFER fssb
;
2772 FILE_CASE_SENSITIVE_INFORMATION fcsi
;
2774 if ((get_access () & (GENERIC_WRITE
| FILE_WRITE_ATTRIBUTES
)) != 0)
2778 status
= NtOpenFile (&fh
, FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
,
2779 pc
.init_reopen_attr (attr
, get_handle ()), &io
,
2780 FILE_SHARE_VALID_FLAGS
,
2781 FILE_OPEN_FOR_BACKUP_INTENT
);
2782 if (!NT_SUCCESS (status
))
2785 __seterrno_from_nt_status (status
);
2789 old_flags
= fs_ioc_getflags ();
2790 if ((old_flags
& FS_FL_SETATTRIBS
) != (flags
& FS_FL_SETATTRIBS
))
2792 fbi
.CreationTime
.QuadPart
2793 = fbi
.LastAccessTime
.QuadPart
2794 = fbi
.LastWriteTime
.QuadPart
2795 = fbi
.ChangeTime
.QuadPart
= 0LL;
2796 fbi
.FileAttributes
= (ULONG
) old_flags
;
2797 fbi
.FileAttributes
&= ~FS_FL_SETATTRIBS
;
2798 fbi
.FileAttributes
|= (flags
& FS_FL_SETATTRIBS
);
2799 if (fbi
.FileAttributes
== 0)
2800 fbi
.FileAttributes
= FILE_ATTRIBUTE_NORMAL
;
2801 status
= NtSetInformationFile (fh
, &io
, &fbi
, sizeof fbi
,
2802 FileBasicInformation
);
2803 if (!NT_SUCCESS (status
))
2805 __seterrno_from_nt_status (status
);
2809 if (!pc
.isdir() && (flags
& FS_SPARSE_FL
) != (old_flags
& FS_SPARSE_FL
))
2811 fssb
.SetSparse
= (flags
& FS_SPARSE_FL
) ? TRUE
: FALSE
;
2812 status
= NtFsControlFile (fh
, NULL
, NULL
, NULL
, &io
,
2813 FSCTL_SET_SPARSE
, &fssb
, sizeof fssb
, NULL
, 0);
2814 if (!NT_SUCCESS (status
))
2816 __seterrno_from_nt_status (status
);
2820 if (pc
.isdir () && (flags
& FS_CASESENS_FL
) != (old_flags
& FS_CASESENS_FL
))
2822 if (wincap
.has_case_sensitive_dirs ()
2823 && !pc
.isremote () && pc
.fs_is_ntfs ())
2825 fcsi
.Flags
= (flags
& FS_CASESENS_FL
)
2826 ? FILE_CS_FLAG_CASE_SENSITIVE_DIR
: 0;
2827 status
= NtSetInformationFile (fh
, &io
, &fcsi
, sizeof fcsi
,
2828 FileCaseSensitiveInformation
);
2829 if (!NT_SUCCESS (status
))
2831 __seterrno_from_nt_status (status
);
2837 set_errno (ENOTSUP
);
2841 if ((flags
& FS_COMPRESSED_FL
) != (old_flags
& FS_COMPRESSED_FL
))
2843 if (fh
!= get_handle ())
2846 if ((get_access () & (GENERIC_WRITE
| GENERIC_READ
))
2847 != (GENERIC_WRITE
| GENERIC_READ
))
2849 status
= NtOpenFile (&fh
, GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
2850 pc
.init_reopen_attr (attr
, get_handle ()), &io
,
2851 FILE_SHARE_VALID_FLAGS
,
2852 FILE_SYNCHRONOUS_IO_NONALERT
2853 | FILE_OPEN_FOR_BACKUP_INTENT
);
2854 if (!NT_SUCCESS (status
))
2857 __seterrno_from_nt_status (status
);
2861 comp
= (flags
& FS_COMPRESSED_FL
)
2862 ? COMPRESSION_FORMAT_DEFAULT
: COMPRESSION_FORMAT_NONE
;
2863 status
= NtFsControlFile (fh
, NULL
, NULL
, NULL
, &io
,
2864 FSCTL_SET_COMPRESSION
, &comp
, sizeof comp
,
2866 if (!NT_SUCCESS (status
))
2868 __seterrno_from_nt_status (status
);
2872 if (!pc
.isdir() && (flags
& FS_ENCRYPT_FL
) != (old_flags
& FS_ENCRYPT_FL
))
2875 PWCHAR path
= tp
.w_get ();
2878 /* EncryptFileW/DecryptFileW needs exclusive access. */
2879 if (fh
!= get_handle ())
2881 NtClose (get_handle ());
2884 pc
.get_wide_win32_path (path
);
2885 cret
= (flags
& FS_ENCRYPT_FL
)
2886 ? EncryptFileW (path
) : DecryptFileW (path
, 0);
2887 status
= NtOpenFile (&fh
, get_access (),
2888 pc
.get_object_attr (attr
, sec_none_nih
), &io
,
2889 FILE_SHARE_VALID_FLAGS
,
2890 FILE_SYNCHRONOUS_IO_NONALERT
2891 | FILE_OPEN_FOR_BACKUP_INTENT
);
2892 if (!NT_SUCCESS (status
))
2894 __seterrno_from_nt_status (status
);
2906 status
= NtQueryInformationFile (fh
, &io
, &fbi
, sizeof fbi
,
2907 FileBasicInformation
);
2908 if (NT_SUCCESS (status
))
2909 pc
.file_attributes (fbi
.FileAttributes
);
2910 if (fh
!= get_handle ())
2916 fhandler_disk_file::ioctl (unsigned int cmd
, void *p
)
2923 case FS_IOC_GETFLAGS
:
2926 uint64_t *fp
= (uint64_t *) p
;
2927 *fp
= fs_ioc_getflags ();
2930 __except (EFAULT
) {}
2933 case FS_IOC_SETFLAGS
:
2936 flags
= *(__uint64_t
*) p
;
2943 if (flags
& ~FS_FL_USER_MODIFIABLE
)
2948 ret
= fs_ioc_setflags (flags
);
2951 ret
= fhandler_base::ioctl (cmd
, p
);
2954 syscall_printf ("%d = ioctl_file(%x, %p)", ret
, cmd
, p
);