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"
28 #define _COMPILING_NEWLIB
31 enum __DIR_mount_type
{
34 __DIR_mount_virt_target
40 const char *parent_dir
;
42 UNICODE_STRING mounts
[MAX_MOUNTS
];
43 bool found
[MAX_MOUNTS
+ 3];
44 UNICODE_STRING cygdrive
;
46 #define __DIR_PROC (MAX_MOUNTS)
47 #define __DIR_CYGDRIVE (MAX_MOUNTS+1)
48 #define __DIR_DEV (MAX_MOUNTS+2)
51 __DIR_mounts (const char *posix_path
)
52 : parent_dir (posix_path
)
54 parent_dir_len
= strlen (parent_dir
);
55 count
= mount_table
->get_mounts_here (parent_dir
, parent_dir_len
, mounts
,
61 for (int i
= 0; i
< count
; ++i
)
62 RtlFreeUnicodeString (&mounts
[i
]);
63 RtlFreeUnicodeString (&cygdrive
);
65 /* For an entry within this dir, check if a mount point exists. */
66 bool check_mount (PUNICODE_STRING fname
)
68 if (parent_dir_len
== 1) /* root dir */
70 if (RtlEqualUnicodeString (fname
, &ro_u_proc
, FALSE
))
72 found
[__DIR_PROC
] = true;
75 if (RtlEqualUnicodeString (fname
, &ro_u_dev
, FALSE
))
77 found
[__DIR_DEV
] = true;
80 if (fname
->Length
/ sizeof (WCHAR
) == mount_table
->cygdrive_len
- 2
81 && RtlEqualUnicodeString (fname
, &cygdrive
, FALSE
))
83 found
[__DIR_CYGDRIVE
] = true;
87 for (int i
= 0; i
< count
; ++i
)
88 if (RtlEqualUnicodeString (fname
, &mounts
[i
], FALSE
))
95 /* On each call, add another mount point within this directory, which is
96 not backed by a real subdir. */
97 __DIR_mount_type
check_missing_mount (PUNICODE_STRING retname
= NULL
)
99 for (int i
= 0; i
< count
; ++i
)
104 *retname
= mounts
[i
];
105 return __DIR_mount_target
;
107 if (parent_dir_len
== 1) /* root dir */
109 if (!found
[__DIR_PROC
])
111 found
[__DIR_PROC
] = true;
113 *retname
= ro_u_proc
;
114 return __DIR_mount_virt_target
;
116 if (!found
[__DIR_DEV
])
118 found
[__DIR_DEV
] = true;
121 return __DIR_mount_virt_target
;
123 if (!found
[__DIR_CYGDRIVE
])
125 found
[__DIR_CYGDRIVE
] = true;
126 if (cygdrive
.Length
> 0)
130 return __DIR_mount_virt_target
;
134 return __DIR_mount_none
;
136 void rewind () { memset (found
, 0, sizeof found
); }
140 path_conv::isgood_inode (ino_t ino
) const
142 /* If the FS doesn't support nonambiguous inode numbers anyway, bail out
144 if (!hasgood_inode ())
146 /* If the inode numbers are 64 bit numbers or if it's a local FS, they
147 are to be trusted. */
148 if (ino
> UINT32_MAX
|| !isremote ())
150 /* The inode numbers returned from a remote NT4 NTFS are ephemeral
154 /* Starting with version 3.5.4, Samba returns the real inode numbers, if
155 the file is on the same device as the root of the share (Samba function
156 get_FileIndex). 32 bit inode numbers returned by older versions (likely
157 < 3.0) are ephemeral. */
158 if (fs_is_samba () && fs
.samba_version () < 0x03050400)
160 /* Otherwise, trust the inode numbers unless proved otherwise. */
164 /* Check reparse point to determine if it should be treated as a
165 posix symlink or as a normal file/directory. Logic is explained
166 in detail in check_reparse_point_target in path.cc. */
168 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr
, bool remote
)
174 UNICODE_STRING symbuf
;
177 status
= NtOpenFile (&reph
, READ_CONTROL
, attr
, &io
, FILE_SHARE_VALID_FLAGS
,
178 FILE_OPEN_FOR_BACKUP_INTENT
| FILE_OPEN_REPARSE_POINT
);
179 if (NT_SUCCESS (status
))
181 PREPARSE_DATA_BUFFER rp
= (PREPARSE_DATA_BUFFER
) tp
.c_get ();
182 ret
= (check_reparse_point_target (reph
, remote
, rp
, &symbuf
) > 0);
189 path_conv::get_ino_by_handle (HANDLE hdl
)
192 FILE_INTERNAL_INFORMATION fii
;
194 if (NT_SUCCESS (NtQueryInformationFile (hdl
, &io
, &fii
, sizeof fii
,
195 FileInternalInformation
))
196 && isgood_inode (fii
.IndexNumber
.QuadPart
))
197 return fii
.IndexNumber
.QuadPart
;
201 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
202 This returns the content of a struct fattr3 as defined in RFC 1813.
203 The content is the NFS equivalent of struct stat. so there's not much
204 to do here except for copying. */
206 fhandler_base::fstat_by_nfs_ea (struct stat
*buf
)
208 fattr3
*nfs_attr
= pc
.nfsattr ();
211 bool ldap_open
= false;
213 if (get_io_handle ())
215 /* NFS stumbles over its own caching. If you write to the file,
216 a subsequent fstat does not return the actual size of the file,
217 but the size at the time the handle has been opened. Unless
218 access through another handle invalidates the caching within the
220 if (get_access () & GENERIC_WRITE
)
221 FlushFileBuffers (get_io_handle ());
222 pc
.get_finfo (get_io_handle ());
224 buf
->st_dev
= nfs_attr
->fsid
;
225 buf
->st_ino
= nfs_attr
->fileid
;
226 buf
->st_mode
= (nfs_attr
->mode
& 0xfff)
227 | nfs_type_mapping
[nfs_attr
->type
& 7];
228 buf
->st_nlink
= nfs_attr
->nlink
;
229 if (cygheap
->pg
.nss_pwd_db ())
231 /* Try to map UNIX uid/gid to Cygwin uid/gid. If there's no mapping in
232 the cache, try to fetch it from the configured RFC 2307 domain (see
233 last comment in cygheap_domain_info::init() for more information) and
234 add it to the mapping cache. */
235 buf
->st_uid
= cygheap
->ugid_cache
.get_uid (nfs_attr
->uid
);
236 if (buf
->st_uid
== ILLEGAL_UID
)
238 uid_t map_uid
= ILLEGAL_UID
;
240 domain
= cygheap
->dom
.get_rfc2307_domain ();
241 if ((ldap_open
= (cldap
.open (domain
) == NO_ERROR
)))
242 map_uid
= cldap
.remap_uid (nfs_attr
->uid
);
243 if (map_uid
== ILLEGAL_UID
)
244 map_uid
= MAP_UNIX_TO_CYGWIN_ID (nfs_attr
->uid
);
245 cygheap
->ugid_cache
.add_uid (nfs_attr
->uid
, map_uid
);
246 buf
->st_uid
= map_uid
;
249 else /* fake files being owned by current user. */
250 buf
->st_uid
= myself
->uid
;
251 if (cygheap
->pg
.nss_grp_db ())
254 buf
->st_gid
= cygheap
->ugid_cache
.get_gid (nfs_attr
->gid
);
255 if (buf
->st_gid
== ILLEGAL_GID
)
257 gid_t map_gid
= ILLEGAL_GID
;
259 domain
= cygheap
->dom
.get_rfc2307_domain ();
260 if ((ldap_open
|| cldap
.open (domain
) == NO_ERROR
))
261 map_gid
= cldap
.remap_gid (nfs_attr
->gid
);
262 if (map_gid
== ILLEGAL_GID
)
263 map_gid
= MAP_UNIX_TO_CYGWIN_ID (nfs_attr
->gid
);
264 cygheap
->ugid_cache
.add_gid (nfs_attr
->gid
, map_gid
);
265 buf
->st_gid
= map_gid
;
268 else /* fake files being owned by current group. */
269 buf
->st_gid
= myself
->gid
;
270 buf
->st_rdev
= makedev (nfs_attr
->rdev
.specdata1
,
271 nfs_attr
->rdev
.specdata2
);
272 buf
->st_size
= nfs_attr
->size
;
273 buf
->st_blksize
= PREFERRED_IO_BLKSIZE
;
274 buf
->st_blocks
= (nfs_attr
->used
+ S_BLKSIZE
- 1) / S_BLKSIZE
;
275 buf
->st_atim
.tv_sec
= nfs_attr
->atime
.tv_sec
;
276 buf
->st_atim
.tv_nsec
= nfs_attr
->atime
.tv_nsec
;
277 buf
->st_mtim
.tv_sec
= nfs_attr
->mtime
.tv_sec
;
278 buf
->st_mtim
.tv_nsec
= nfs_attr
->mtime
.tv_nsec
;
279 buf
->st_ctim
.tv_sec
= nfs_attr
->ctime
.tv_sec
;
280 buf
->st_ctim
.tv_nsec
= nfs_attr
->ctime
.tv_nsec
;
285 fhandler_base::fstat_by_handle (struct stat
*buf
)
287 HANDLE h
= get_stat_handle ();
290 /* If the file has been opened for other purposes than stat, we can't rely
291 on the information stored in pc.fai. So we overwrite them here. */
292 if (get_io_handle ())
294 status
= pc
.get_finfo (h
);
295 if (!NT_SUCCESS (status
))
297 debug_printf ("%y = NtQueryInformationFile(%S, FileAllInformation)",
298 status
, pc
.get_nt_native_path ());
302 if (pc
.isgood_inode (pc
.fai ()->InternalInformation
.IndexNumber
.QuadPart
))
303 ino
= pc
.fai ()->InternalInformation
.IndexNumber
.QuadPart
;
304 return fstat_helper (buf
);
308 fhandler_base::fstat_by_name (struct stat
*buf
)
311 OBJECT_ATTRIBUTES attr
;
313 UNICODE_STRING dirname
;
314 UNICODE_STRING basename
;
317 FILE_ID_BOTH_DIR_INFORMATION fdi
;
318 WCHAR buf
[NAME_MAX
+ 1];
321 if (!ino
&& pc
.hasgood_inode () && !pc
.has_buggy_fileid_dirinfo ())
323 RtlSplitUnicodePath (pc
.get_nt_native_path (), &dirname
, &basename
);
324 InitializeObjectAttributes (&attr
, &dirname
, pc
.objcaseinsensitive (),
326 status
= NtOpenFile (&dir
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
327 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
328 FILE_SYNCHRONOUS_IO_NONALERT
329 | FILE_OPEN_FOR_BACKUP_INTENT
330 | FILE_DIRECTORY_FILE
);
331 if (!NT_SUCCESS (status
))
332 debug_printf ("%y = NtOpenFile(%S)", status
,
333 pc
.get_nt_native_path ());
336 status
= NtQueryDirectoryFile (dir
, NULL
, NULL
, NULL
, &io
,
337 &fdi_buf
.fdi
, sizeof fdi_buf
,
338 FileIdBothDirectoryInformation
,
339 TRUE
, &basename
, TRUE
);
341 if (!NT_SUCCESS (status
))
342 debug_printf ("%y = NtQueryDirectoryFile(%S)", status
,
343 pc
.get_nt_native_path ());
345 ino
= fdi_buf
.fdi
.FileId
.QuadPart
;
348 return fstat_helper (buf
);
352 fhandler_base::fstat_fs (struct stat
*buf
)
356 int open_flags
= O_RDONLY
| O_BINARY
;
358 if (get_stat_handle ())
360 if (!nohandle () && !is_fs_special ())
361 res
= pc
.fs_is_nfs () ? fstat_by_nfs_ea (buf
) : fstat_by_handle (buf
);
363 res
= fstat_by_name (buf
);
366 /* First try to open with generic read access. This allows to read the file
367 in fstat_helper (when checking for executability) without having to
368 re-open it. Opening a file can take a lot of time on network drives
369 so we try to avoid that. */
370 oret
= open_fs (open_flags
, 0);
373 query_open (query_read_attributes
);
374 oret
= open_fs (open_flags
, 0);
378 /* We now have a valid handle, regardless of the "nohandle" state.
379 Since fhandler_base::close only calls CloseHandle if !nohandle,
380 we have to set it to false before calling close and restore
381 the state afterwards. */
382 res
= pc
.fs_is_nfs () ? fstat_by_nfs_ea (buf
) : fstat_by_handle (buf
);
383 bool no_handle
= nohandle ();
386 nohandle (no_handle
);
387 set_io_handle (NULL
);
390 res
= fstat_by_name (buf
);
396 fhandler_base::fstat_helper (struct stat
*buf
)
399 FILE_COMPRESSION_INFORMATION fci
;
400 HANDLE h
= get_stat_handle ();
401 PFILE_ALL_INFORMATION pfai
= pc
.fai ();
402 ULONG attributes
= pc
.file_attributes ();
404 to_timestruc_t (&pfai
->BasicInformation
.LastAccessTime
, &buf
->st_atim
);
405 to_timestruc_t (&pfai
->BasicInformation
.LastWriteTime
, &buf
->st_mtim
);
406 /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
407 (FAT for instance). If so, it's faked using LastWriteTime. */
408 to_timestruc_t (pfai
->BasicInformation
.ChangeTime
.QuadPart
409 ? &pfai
->BasicInformation
.ChangeTime
410 : &pfai
->BasicInformation
.LastWriteTime
,
412 to_timestruc_t (&pfai
->BasicInformation
.CreationTime
, &buf
->st_birthtim
);
413 buf
->st_dev
= get_dev ();
414 /* CV 2011-01-13: Observations on the Cygwin mailing list point to an
415 interesting behaviour in some Windows versions. Apparently the size of
416 a directory is computed at the time the directory is first scanned. This
417 can result in two subsequent NtQueryInformationFile calls to return size
418 0 in the first call and size > 0 in the second call. This in turn can
419 affect applications like newer tar.
420 FIXME: Is the allocation size affected as well? */
421 buf
->st_size
= pc
.isdir ()
423 : (off_t
) pfai
->StandardInformation
.EndOfFile
.QuadPart
;
424 /* The number of links to a directory includes the number of subdirectories
425 in the directory, since all those subdirectories point to it. However,
426 this is painfully slow, so we do without it. */
427 buf
->st_nlink
= pc
.fai()->StandardInformation
.NumberOfLinks
;
429 /* Enforce namehash as inode number on untrusted file systems. */
430 buf
->st_ino
= ino
?: get_ino ();
432 buf
->st_blksize
= PREFERRED_IO_BLKSIZE
;
434 if (buf
->st_size
== 0
435 && pfai
->StandardInformation
.AllocationSize
.QuadPart
== 0LL)
436 /* File is empty and no blocks are preallocated. */
438 else if (pfai
->StandardInformation
.AllocationSize
.QuadPart
> 0LL)
439 /* A successful NtQueryInformationFile returns the allocation size
440 correctly for compressed and sparse files as well.
441 Allocation size 0 is ignored here because (at least) Windows 10
442 1607 always returns 0 for CompactOS compressed files. */
443 buf
->st_blocks
= (pfai
->StandardInformation
.AllocationSize
.QuadPart
444 + S_BLKSIZE
- 1) / S_BLKSIZE
;
445 else if ((pfai
->StandardInformation
.AllocationSize
.QuadPart
== 0LL
446 || ::has_attribute (attributes
, FILE_ATTRIBUTE_COMPRESSED
447 | FILE_ATTRIBUTE_SPARSE_FILE
))
448 && h
&& !is_fs_special ()
449 && !NtQueryInformationFile (h
, &st
, (PVOID
) &fci
, sizeof fci
,
450 FileCompressionInformation
))
451 /* Otherwise we request the actual amount of bytes allocated for
452 compressed, sparsed and CompactOS files. */
453 buf
->st_blocks
= (fci
.CompressedFileSize
.QuadPart
+ S_BLKSIZE
- 1)
456 /* Otherwise compute no. of blocks from file size. */
457 buf
->st_blocks
= (buf
->st_size
+ S_BLKSIZE
- 1) / S_BLKSIZE
;
460 /* Using a side effect: get_file_attributes checks for directory.
461 This is used, to set S_ISVTX, if needed. */
463 buf
->st_mode
= S_IFDIR
;
464 else if (pc
.issymlink ())
466 buf
->st_size
= pc
.get_symlink_length ();
467 /* symlinks are everything for everyone! */
468 buf
->st_mode
= S_IFLNK
| S_IRWXU
| S_IRWXG
| S_IRWXO
;
469 get_file_attribute (h
, pc
, NULL
,
470 &buf
->st_uid
, &buf
->st_gid
);
473 else if (pc
.issocket ())
474 buf
->st_mode
= S_IFSOCK
;
476 if (!get_file_attribute (is_fs_special () && !pc
.issocket () ? NULL
: h
, pc
,
477 &buf
->st_mode
, &buf
->st_uid
, &buf
->st_gid
))
479 /* If read-only attribute is set, modify ntsec return value */
480 if (::has_attribute (attributes
, FILE_ATTRIBUTE_READONLY
)
481 && !pc
.isdir () && !pc
.issymlink ())
482 buf
->st_mode
&= ~(S_IWUSR
| S_IWGRP
| S_IWOTH
);
484 if (buf
->st_mode
& S_IFMT
)
486 else if (!is_fs_special ())
487 buf
->st_mode
|= S_IFREG
;
490 buf
->st_dev
= buf
->st_rdev
= dev ();
491 buf
->st_mode
= dev ().mode ();
497 buf
->st_mode
|= STD_RBITS
;
499 if (!::has_attribute (attributes
, FILE_ATTRIBUTE_READONLY
))
500 buf
->st_mode
|= STD_WBITS
;
501 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
504 buf
->st_mode
|= S_IFDIR
| STD_WBITS
| STD_XBITS
;
505 else if (buf
->st_mode
& S_IFMT
)
507 else if (is_fs_special ())
509 buf
->st_dev
= buf
->st_rdev
= dev ();
510 buf
->st_mode
= dev ().mode ();
515 buf
->st_mode
|= S_IFREG
;
516 /* Check suffix for executable file. */
517 if (pc
.exec_state () != is_executable
)
519 PUNICODE_STRING path
= pc
.get_nt_native_path ();
521 if (RtlEqualUnicodePathSuffix (path
, &ro_u_exe
, TRUE
)
522 || RtlEqualUnicodePathSuffix (path
, &ro_u_lnk
, TRUE
)
523 || RtlEqualUnicodePathSuffix (path
, &ro_u_com
, 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;
603 FILE_FS_FULL_SIZE_INFORMATION full_fsi
;
604 /* We must not use the stat handle here, even if it exists. The handle
605 has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
606 mount point, it points to the FS of the mount point, rather than to the
608 HANDLE fh
= get_handle ();
612 OBJECT_ATTRIBUTES attr
;
613 opened
= NT_SUCCESS (NtOpenFile (&fh
, READ_CONTROL
,
614 pc
.get_object_attr (attr
, sec_none_nih
),
615 &io
, FILE_SHARE_VALID_FLAGS
,
616 FILE_OPEN_FOR_BACKUP_INTENT
));
619 /* Can't open file. Try again with parent dir. */
620 UNICODE_STRING dirname
;
621 RtlSplitUnicodePath (pc
.get_nt_native_path (), &dirname
, NULL
);
622 attr
.ObjectName
= &dirname
;
623 opened
= NT_SUCCESS (NtOpenFile (&fh
, READ_CONTROL
, &attr
, &io
,
624 FILE_SHARE_VALID_FLAGS
,
625 FILE_OPEN_FOR_BACKUP_INTENT
));
631 sfs
->f_files
= ULONG_MAX
;
632 sfs
->f_ffree
= ULONG_MAX
;
633 sfs
->f_favail
= ULONG_MAX
;
634 sfs
->f_fsid
= pc
.fs_serial_number ();
635 sfs
->f_flag
= pc
.fs_flags ();
636 sfs
->f_namemax
= pc
.fs_name_len ();
637 /* Get allocation related information. */
638 status
= NtQueryVolumeInformationFile (fh
, &io
, &full_fsi
, sizeof full_fsi
,
639 FileFsFullSizeInformation
);
640 if (NT_SUCCESS (status
))
642 sfs
->f_bsize
= full_fsi
.BytesPerSector
* full_fsi
.SectorsPerAllocationUnit
;
643 sfs
->f_frsize
= sfs
->f_bsize
;
644 sfs
->f_blocks
= (fsblkcnt_t
) full_fsi
.TotalAllocationUnits
.QuadPart
;
645 sfs
->f_bfree
= (fsblkcnt_t
)
646 full_fsi
.ActualAvailableAllocationUnits
.QuadPart
;
647 sfs
->f_bavail
= (fsblkcnt_t
)
648 full_fsi
.CallerAvailableAllocationUnits
.QuadPart
;
649 if (sfs
->f_bfree
> sfs
->f_bavail
)
651 /* Quotas active. We can't trust TotalAllocationUnits. */
652 NTFS_VOLUME_DATA_BUFFER nvdb
;
654 status
= NtFsControlFile (fh
, NULL
, NULL
, NULL
, &io
,
655 FSCTL_GET_NTFS_VOLUME_DATA
,
656 NULL
, 0, &nvdb
, sizeof nvdb
);
657 if (!NT_SUCCESS (status
))
658 debug_printf ("%y = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
659 status
, pc
.get_nt_native_path ());
661 sfs
->f_blocks
= (fsblkcnt_t
) nvdb
.TotalClusters
.QuadPart
;
665 else if (status
== STATUS_INVALID_PARAMETER
/* Netapp */
666 || status
== STATUS_INVALID_INFO_CLASS
)
668 FILE_FS_SIZE_INFORMATION fsi
;
669 status
= NtQueryVolumeInformationFile (fh
, &io
, &fsi
, sizeof fsi
,
670 FileFsSizeInformation
);
671 if (NT_SUCCESS (status
))
673 sfs
->f_bsize
= fsi
.BytesPerSector
* fsi
.SectorsPerAllocationUnit
;
674 sfs
->f_frsize
= sfs
->f_bsize
;
675 sfs
->f_blocks
= (fsblkcnt_t
) fsi
.TotalAllocationUnits
.QuadPart
;
676 sfs
->f_bfree
= sfs
->f_bavail
=
677 (fsblkcnt_t
) fsi
.AvailableAllocationUnits
.QuadPart
;
681 debug_printf ("%y = NtQueryVolumeInformationFile"
682 "(%S, FileFsSizeInformation)",
683 status
, pc
.get_nt_native_path ());
686 debug_printf ("%y = NtQueryVolumeInformationFile"
687 "(%S, FileFsFullSizeInformation)",
688 status
, pc
.get_nt_native_path ());
692 syscall_printf ("%d = fstatvfs(%s, %p)", ret
, get_name (), sfs
);
697 fhandler_disk_file::fchmod (mode_t mode
)
699 extern int chmod_device (path_conv
& pc
, mode_t mode
);
705 if (pc
.is_fs_special ())
706 return chmod_device (pc
, mode
);
710 query_open (query_write_dac
);
711 if (!(oret
= open (O_BINARY
, 0)))
713 /* Need WRITE_DAC to write ACLs. */
716 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
717 query_open (query_write_attributes
);
718 if (!(oret
= open (O_BINARY
, 0)))
725 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
726 Only type and mode have to be set. Apparently type isn't checked
727 for consistency, so it's sufficent to set it to NF3REG all the time. */
729 FILE_FULL_EA_INFORMATION ffei
;
730 char buf
[sizeof (NFS_V3_ATTR
) + sizeof (fattr3
)];
732 ffei_buf
.ffei
.NextEntryOffset
= 0;
733 ffei_buf
.ffei
.Flags
= 0;
734 ffei_buf
.ffei
.EaNameLength
= sizeof (NFS_V3_ATTR
) - 1;
735 ffei_buf
.ffei
.EaValueLength
= sizeof (fattr3
);
736 strcpy (ffei_buf
.ffei
.EaName
, NFS_V3_ATTR
);
737 fattr3
*nfs_attr
= (fattr3
*) (ffei_buf
.ffei
.EaName
738 + ffei_buf
.ffei
.EaNameLength
+ 1);
739 memset (nfs_attr
, 0, sizeof (fattr3
));
740 nfs_attr
->type
= NF3REG
;
741 nfs_attr
->mode
= mode
;
742 status
= NtSetEaFile (get_handle (), &io
,
743 &ffei_buf
.ffei
, sizeof ffei_buf
);
744 if (!NT_SUCCESS (status
))
745 __seterrno_from_nt_status (status
);
753 security_descriptor sd
, sd_ret
;
758 bool standard_acl
= false;
761 if (!get_file_sd (get_handle (), pc
, sd
, false))
763 aclp
= (aclent_t
*) tp
.c_get ();
764 if ((nentries
= get_posix_access (sd
, NULL
, &uid
, &gid
,
765 aclp
, MAX_ACL_ENTRIES
,
766 &standard_acl
)) >= 0)
768 /* Overwrite ACL permissions as required by POSIX 1003.1e
770 aclp
[0].a_perm
= (mode
>> 6) & S_IRWXO
;
772 /* POSIXly correct: If CLASS_OBJ is present, chmod only modifies
773 CLASS_OBJ, not GROUP_OBJ.
775 Deliberate deviation from POSIX 1003.1e: If the ACL is a
776 "standard" ACL, that is, it only contains POSIX permissions
777 as well as entries for the Administrators group and SYSTEM,
778 then it's kind of a POSIX-only ACL in a twisted, Windowsy
779 way. If so, we change GROUP_OBJ and CLASS_OBJ perms. */
781 && (idx
= searchace (aclp
, nentries
, GROUP_OBJ
)) >= 0)
782 aclp
[idx
].a_perm
= (mode
>> 3) & S_IRWXO
;
783 if (nentries
> MIN_ACL_ENTRIES
784 && (idx
= searchace (aclp
, nentries
, CLASS_OBJ
)) >= 0)
785 aclp
[idx
].a_perm
= (mode
>> 3) & S_IRWXO
;
787 if ((idx
= searchace (aclp
, nentries
, OTHER_OBJ
)) >= 0)
788 aclp
[idx
].a_perm
= mode
& S_IRWXO
;
791 if (set_posix_access (mode
, uid
, gid
, aclp
, nentries
, sd_ret
,
793 ret
= set_file_sd (get_handle (), pc
, sd_ret
, false);
798 /* If the mode has any write bits set, the DOS R/O flag is in the way. */
799 if (mode
& (S_IWUSR
| S_IWGRP
| S_IWOTH
))
800 pc
&= (DWORD
) ~FILE_ATTRIBUTE_READONLY
;
801 else if (!pc
.has_acls ()) /* Never set DOS R/O if security is used. */
802 pc
|= (DWORD
) FILE_ATTRIBUTE_READONLY
;
804 pc
|= (DWORD
) FILE_ATTRIBUTE_SYSTEM
;
806 status
= NtSetAttributesFile (get_handle (), pc
.file_attributes ());
807 /* MVFS needs a good amount of kicking to be convinced that it has to write
808 back metadata changes and to invalidate the cached metadata information
809 stored for the given handle. This method to open a second handle to
810 the file and write the same metadata information twice has been found
811 experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
812 if (pc
.fs_is_mvfs () && NT_SUCCESS (status
) && !oret
)
814 OBJECT_ATTRIBUTES attr
;
817 if (NT_SUCCESS (NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
,
818 pc
.init_reopen_attr (attr
, get_handle ()),
819 &io
, FILE_SHARE_VALID_FLAGS
,
820 FILE_OPEN_FOR_BACKUP_INTENT
)))
822 NtSetAttributesFile (fh
, pc
.file_attributes ());
826 /* Correct NTFS security attributes have higher priority */
829 if (!NT_SUCCESS (status
))
830 __seterrno_from_nt_status (status
);
843 fhandler_disk_file::fchown (uid_t uid
, gid_t gid
)
847 security_descriptor sd
, sd_ret
;
848 mode_t attr
= pc
.isdir () ? S_IFDIR
: 0;
857 /* fake - if not supported, pretend we're like win95
858 where it just works */
859 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
865 query_open (query_write_control
);
866 if (!(oret
= fhandler_disk_file::open (O_BINARY
, 0)))
870 if (get_file_sd (get_handle (), pc
, sd
, false))
873 aclp
= (aclent_t
*) tp
.c_get ();
874 if ((nentries
= get_posix_access (sd
, &attr
, &old_uid
, &old_gid
,
875 aclp
, MAX_ACL_ENTRIES
)) < 0)
878 if (uid
== ILLEGAL_UID
)
880 if (gid
== ILLEGAL_GID
)
882 if (uid
== old_uid
&& gid
== old_gid
)
888 /* Windows ACLs can contain permissions for one group, while being owned by
889 another user/group. The permission bits returned above are pretty much
890 useless then. Creating a new ACL with these useless permissions results
891 in a potentially broken symlink. So what we do here is to set the
892 underlying permissions of symlinks to a sensible value which allows the
893 world to read the symlink and only the new owner to change it. */
895 for (int idx
= 0; idx
< nentries
; ++idx
)
897 aclp
[idx
].a_perm
|= S_IROTH
;
898 if (aclp
[idx
].a_type
& USER_OBJ
)
899 aclp
[idx
].a_perm
|= S_IWOTH
;
902 if (set_posix_access (attr
, uid
, gid
, aclp
, nentries
, sd_ret
,
904 ret
= set_file_sd (get_handle (), pc
, sd_ret
, true);
906 /* If you're running a Samba server with no winbind, the uid<->SID mapping
907 is disfunctional. Even trying to chown to your own account fails since
908 the account used on the server is the UNIX account which gets used for
909 the standard user mapping. This is a default mechanism which doesn't
910 know your real Windows SID. There are two possible error codes in
911 different Samba releases for this situation, one of them unfortunately
912 the not very significant STATUS_ACCESS_DENIED. Instead of relying on
913 the error codes, we're using the below very simple heuristic.
914 If set_file_sd failed, and the original user account was either already
915 unknown, or one of the standard UNIX accounts, we're faking success. */
916 if (ret
== -1 && pc
.fs_is_samba ())
921 || ((sid
= sidfromuid (old_uid
, NULL
)) != NO_SID
922 && RtlEqualPrefixSid (sid
,
923 well_known_samba_unix_user_fake_sid
)))
925 debug_printf ("Faking chown worked on standalone Samba");
938 fhandler_disk_file::facl (int cmd
, int nentries
, aclent_t
*aclbufp
)
950 /* Open for writing required to be able to set ctime
951 (even though setting the ACL is just pretended). */
953 oret
= open (O_WRONLY
| O_BINARY
, 0);
959 else if (nentries
< MIN_ACL_ENTRIES
)
966 aclbufp
[0].a_type
= USER_OBJ
;
967 aclbufp
[0].a_id
= st
.st_uid
;
968 aclbufp
[0].a_perm
= (st
.st_mode
& S_IRWXU
) >> 6;
969 aclbufp
[1].a_type
= GROUP_OBJ
;
970 aclbufp
[1].a_id
= st
.st_gid
;
971 aclbufp
[1].a_perm
= (st
.st_mode
& S_IRWXG
) >> 3;
972 aclbufp
[2].a_type
= OTHER_OBJ
;
973 aclbufp
[2].a_id
= ILLEGAL_GID
;
974 aclbufp
[2].a_perm
= st
.st_mode
& S_IRWXO
;
975 res
= MIN_ACL_ENTRIES
;
980 res
= MIN_ACL_ENTRIES
;
989 if ((cmd
== SETACL
&& !get_handle ())
990 || (cmd
!= SETACL
&& !get_stat_handle ()))
992 query_open (cmd
== SETACL
? query_write_dac
: query_read_control
);
993 if (!(oret
= open (O_BINARY
, 0)))
995 if (cmd
== GETACL
|| cmd
== GETACLCNT
)
996 goto cant_access_acl
;
1003 if (!aclsort32 (nentries
, 0, aclbufp
))
1006 res
= setacl (get_handle (), pc
, nentries
, aclbufp
, rw
);
1010 FILE_BASIC_INFORMATION fbi
;
1011 fbi
.CreationTime
.QuadPart
1012 = fbi
.LastAccessTime
.QuadPart
1013 = fbi
.LastWriteTime
.QuadPart
1014 = fbi
.ChangeTime
.QuadPart
= 0LL;
1015 fbi
.FileAttributes
= (pc
.file_attributes ()
1016 & ~FILE_ATTRIBUTE_READONLY
)
1017 ?: FILE_ATTRIBUTE_NORMAL
;
1018 NtSetInformationFile (get_handle (), &io
, &fbi
, sizeof fbi
,
1019 FileBasicInformation
);
1027 res
= getacl (get_stat_handle (), pc
, nentries
, aclbufp
);
1028 /* For this ENOSYS case, see security.cc:get_file_attribute(). */
1029 if (res
== -1 && get_errno () == ENOSYS
)
1030 goto cant_access_acl
;
1034 res
= getacl (get_stat_handle (), pc
, 0, NULL
);
1036 if (res
== -1 && get_errno () == ENOSYS
)
1037 goto cant_access_acl
;
1052 fhandler_disk_file::fgetxattr (const char *name
, void *value
, size_t size
)
1054 if (pc
.is_fs_special ())
1056 set_errno (ENOTSUP
);
1059 return read_ea (get_handle (), pc
, name
, (char *) value
, size
);
1063 fhandler_disk_file::fsetxattr (const char *name
, const void *value
, size_t size
,
1066 if (pc
.is_fs_special ())
1068 set_errno (ENOTSUP
);
1071 return write_ea (get_handle (), pc
, name
, (const char *) value
, size
, flags
);
1075 fhandler_disk_file::fadvise (off_t offset
, off_t length
, int advice
)
1077 if (advice
< POSIX_FADV_NORMAL
|| advice
> POSIX_FADV_NOREUSE
)
1080 /* Windows only supports advice flags for the whole file. We're using
1081 a simplified test here so that we don't have to ask for the actual
1082 file size. Length == 0 means all bytes starting at offset anyway.
1083 So we only actually follow the advice, if it's given for offset == 0. */
1087 /* We only support normal and sequential mode for now. Everything which
1088 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1089 if (advice
!= POSIX_FADV_SEQUENTIAL
)
1090 advice
= POSIX_FADV_NORMAL
;
1093 FILE_MODE_INFORMATION fmi
;
1094 NTSTATUS status
= NtQueryInformationFile (get_handle (), &io
,
1096 FileModeInformation
);
1097 if (NT_SUCCESS (status
))
1099 fmi
.Mode
&= ~FILE_SEQUENTIAL_ONLY
;
1100 if (advice
== POSIX_FADV_SEQUENTIAL
)
1101 fmi
.Mode
|= FILE_SEQUENTIAL_ONLY
;
1102 status
= NtSetInformationFile (get_handle (), &io
, &fmi
, sizeof fmi
,
1103 FileModeInformation
);
1104 if (NT_SUCCESS (status
))
1106 __seterrno_from_nt_status (status
);
1109 return geterrno_from_nt_status (status
);
1113 fhandler_disk_file::ftruncate (off_t length
, bool allow_truncate
)
1117 if (length
< 0 || !get_handle ())
1119 else if (pc
.isdir ())
1121 else if (!(get_access () & GENERIC_WRITE
))
1127 FILE_STANDARD_INFORMATION fsi
;
1128 FILE_END_OF_FILE_INFORMATION feofi
;
1130 status
= NtQueryInformationFile (get_handle (), &io
, &fsi
, sizeof fsi
,
1131 FileStandardInformation
);
1132 if (!NT_SUCCESS (status
))
1133 return geterrno_from_nt_status (status
);
1135 /* If called through posix_fallocate, silently succeed if length
1136 is less than the file's actual length. */
1137 if (!allow_truncate
&& length
< fsi
.EndOfFile
.QuadPart
)
1140 feofi
.EndOfFile
.QuadPart
= length
;
1141 /* Create sparse files only when called through ftruncate, not when
1142 called through posix_fallocate. */
1143 if (allow_truncate
&& pc
.support_sparse ()
1144 && !has_attribute (FILE_ATTRIBUTE_SPARSE_FILE
)
1145 && length
>= fsi
.EndOfFile
.QuadPart
+ (128 * 1024))
1147 status
= NtFsControlFile (get_handle (), NULL
, NULL
, NULL
, &io
,
1148 FSCTL_SET_SPARSE
, NULL
, 0, NULL
, 0);
1149 if (NT_SUCCESS (status
))
1150 pc
.file_attributes (pc
.file_attributes ()
1151 | FILE_ATTRIBUTE_SPARSE_FILE
);
1152 syscall_printf ("%y = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1153 status
, pc
.get_nt_native_path ());
1155 status
= NtSetInformationFile (get_handle (), &io
,
1156 &feofi
, sizeof feofi
,
1157 FileEndOfFileInformation
);
1158 if (!NT_SUCCESS (status
))
1159 res
= geterrno_from_nt_status (status
);
1165 fhandler_disk_file::link (const char *newpath
)
1167 size_t nlen
= strlen (newpath
);
1168 path_conv
newpc (newpath
, PC_SYM_NOFOLLOW
| PC_POSIX
| PC_NULLEMPTY
, stat_suffixes
);
1171 set_errno (newpc
.error
);
1175 if (newpc
.exists ())
1177 syscall_printf ("file '%S' exists?", newpc
.get_nt_native_path ());
1182 if (isdirsep (newpath
[nlen
- 1]) || has_dot_last_component (newpath
, false))
1188 char new_buf
[nlen
+ 5];
1191 /* If the original file is a lnk special file (except for sockets),
1192 and if the original file has a .lnk suffix, add one to the hardlink
1194 if (pc
.is_lnk_special () && !pc
.issocket ()
1195 && RtlEqualUnicodePathSuffix (pc
.get_nt_native_path (),
1198 /* Shortcut hack. */
1199 stpcpy (stpcpy (new_buf
, newpath
), ".lnk");
1201 newpc
.check (newpath
, PC_SYM_NOFOLLOW
);
1203 else if (!pc
.isdir ()
1205 && RtlEqualUnicodePathSuffix (pc
.get_nt_native_path (),
1207 && !RtlEqualUnicodePathSuffix (newpc
.get_nt_native_path (),
1210 /* Executable hack. */
1211 stpcpy (stpcpy (new_buf
, newpath
), ".exe");
1213 newpc
.check (newpath
, PC_SYM_NOFOLLOW
);
1217 /* We only need READ_CONTROL access so the handle returned in pc is
1218 sufficient. And if the file couldn't be opened with READ_CONTROL
1219 access in path_conv, we won't be able to do it here anyway. */
1220 HANDLE fh
= get_stat_handle ();
1226 PUNICODE_STRING tgt
= newpc
.get_nt_native_path ();
1227 ULONG size
= sizeof (FILE_LINK_INFORMATION
) + tgt
->Length
;
1228 PFILE_LINK_INFORMATION pfli
= (PFILE_LINK_INFORMATION
) alloca (size
);
1229 pfli
->ReplaceIfExists
= FALSE
;
1230 pfli
->RootDirectory
= NULL
;
1231 memcpy (pfli
->FileName
, tgt
->Buffer
, pfli
->FileNameLength
= tgt
->Length
);
1235 status
= NtSetInformationFile (fh
, &io
, pfli
, size
, FileLinkInformation
);
1236 if (!NT_SUCCESS (status
))
1238 if (status
== STATUS_INVALID_DEVICE_REQUEST
1239 || status
== STATUS_NOT_SUPPORTED
)
1240 /* FS doesn't support hard links. Linux returns EPERM. */
1243 __seterrno_from_nt_status (status
);
1246 else if (pc
.file_attributes () & FILE_ATTRIBUTE_TEMPORARY
)
1248 /* If the original file has been opened with O_TMPFILE the file has
1249 FILE_ATTRIBUTE_TEMPORARY set. After a successful hardlink the
1250 file is not temporary anymore in the usual sense. So we remove
1251 FILE_ATTRIBUTE_TEMPORARY here, even if this makes the original file
1252 visible in directory enumeration. */
1253 OBJECT_ATTRIBUTES attr
;
1254 status
= NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
,
1255 pc
.init_reopen_attr (attr
, fh
), &io
,
1256 FILE_SHARE_VALID_FLAGS
, FILE_OPEN_FOR_BACKUP_INTENT
);
1257 if (!NT_SUCCESS (status
))
1258 debug_printf ("Opening for removing TEMPORARY attrib failed, "
1259 "status = %y", status
);
1262 FILE_BASIC_INFORMATION fbi
;
1264 fbi
.CreationTime
.QuadPart
= fbi
.LastAccessTime
.QuadPart
1265 = fbi
.LastWriteTime
.QuadPart
= fbi
.ChangeTime
.QuadPart
= 0LL;
1266 fbi
.FileAttributes
= (pc
.file_attributes ()
1267 & ~FILE_ATTRIBUTE_TEMPORARY
)
1268 ?: FILE_ATTRIBUTE_NORMAL
;
1269 status
= NtSetInformationFile (fh
, &io
, &fbi
, sizeof fbi
,
1270 FileBasicInformation
);
1271 if (!NT_SUCCESS (status
))
1272 debug_printf ("Removing the TEMPORARY attrib failed, status = %y",
1281 fhandler_disk_file::utimens (const struct timespec
*tvp
)
1283 return utimens_fs (tvp
);
1287 fhandler_base::utimens_fs (const struct timespec
*tvp
)
1289 struct timespec timeofday
;
1290 struct timespec tmp
[2];
1291 bool closeit
= false;
1295 query_open (query_write_attributes
);
1296 if (!open_fs (O_BINARY
, 0))
1298 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1299 to change the timestamps. Unfortunately it's not sufficient for a
1300 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1301 for writing, though this fails for R/O files of course. */
1302 query_open (no_query
);
1303 if (!open_fs (O_WRONLY
| O_BINARY
, 0))
1305 syscall_printf ("Opening file failed");
1312 clock_gettime (CLOCK_REALTIME
, &timeofday
);
1314 tmp
[1] = tmp
[0] = timeofday
;
1317 if ((tvp
[0].tv_nsec
< UTIME_NOW
|| tvp
[0].tv_nsec
> 999999999L)
1318 || (tvp
[1].tv_nsec
< UTIME_NOW
|| tvp
[1].tv_nsec
> 999999999L))
1325 tmp
[0] = (tvp
[0].tv_nsec
== UTIME_NOW
) ? timeofday
: tvp
[0];
1326 tmp
[1] = (tvp
[1].tv_nsec
== UTIME_NOW
) ? timeofday
: tvp
[1];
1328 debug_printf ("incoming lastaccess %ly %ly", tmp
[0].tv_sec
, tmp
[0].tv_nsec
);
1331 FILE_BASIC_INFORMATION fbi
;
1333 fbi
.CreationTime
.QuadPart
= 0LL;
1334 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1335 timespec_to_filetime (&tmp
[0], &fbi
.LastAccessTime
);
1336 timespec_to_filetime (&tmp
[1], &fbi
.LastWriteTime
);
1337 fbi
.ChangeTime
.QuadPart
= 0LL;
1338 fbi
.FileAttributes
= 0;
1339 NTSTATUS status
= NtSetInformationFile (get_handle (), &io
, &fbi
, sizeof fbi
,
1340 FileBasicInformation
);
1341 /* For this special case for MVFS see the comment in
1342 fhandler_disk_file::fchmod. */
1343 if (pc
.fs_is_mvfs () && NT_SUCCESS (status
) && !closeit
)
1345 OBJECT_ATTRIBUTES attr
;
1348 if (NT_SUCCESS (NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
,
1349 pc
.init_reopen_attr (attr
, get_handle ()),
1350 &io
, FILE_SHARE_VALID_FLAGS
,
1351 FILE_OPEN_FOR_BACKUP_INTENT
)))
1353 NtSetInformationFile (fh
, &io
, &fbi
, sizeof fbi
,
1354 FileBasicInformation
);
1360 /* Opening a directory on a 9x share from a NT machine works(!), but
1361 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1362 if (!NT_SUCCESS (status
) && status
!= STATUS_NOT_SUPPORTED
)
1364 __seterrno_from_nt_status (status
);
1370 fhandler_disk_file::fhandler_disk_file () :
1371 fhandler_base (), prw_handle (NULL
)
1375 fhandler_disk_file::fhandler_disk_file (path_conv
&pc
) :
1376 fhandler_base (), prw_handle (NULL
)
1382 fhandler_disk_file::open (int flags
, mode_t mode
)
1384 return open_fs (flags
, mode
);
1388 fhandler_disk_file::close ()
1390 /* Close extra pread/pwrite handle, if it exists. */
1392 NtClose (prw_handle
);
1393 return fhandler_base::close ();
1397 fhandler_disk_file::fcntl (int cmd
, intptr_t arg
)
1403 case F_LCK_MANDATORY
: /* Mandatory locking only works on files. */
1404 mandatory_locking (!!arg
);
1405 need_fork_fixup (true);
1409 res
= fhandler_base::fcntl (cmd
, arg
);
1416 fhandler_disk_file::dup (fhandler_base
*child
, int flags
)
1418 fhandler_disk_file
*fhc
= (fhandler_disk_file
*) child
;
1420 int ret
= fhandler_base::dup (child
, flags
);
1421 if (!ret
&& prw_handle
1422 && !DuplicateHandle (GetCurrentProcess (), prw_handle
,
1423 GetCurrentProcess (), &fhc
->prw_handle
,
1424 0, TRUE
, DUPLICATE_SAME_ACCESS
))
1425 fhc
->prw_handle
= NULL
;
1430 fhandler_disk_file::fixup_after_fork (HANDLE parent
)
1433 mandatory_locking (false);
1434 fhandler_base::fixup_after_fork (parent
);
1438 fhandler_base::open_fs (int flags
, mode_t mode
)
1440 /* Unfortunately NT allows to open directories for writing, but that's
1441 disallowed according to SUSv3. */
1442 if (pc
.isdir () && (flags
& O_ACCMODE
) != O_RDONLY
)
1448 bool new_file
= !exists ();
1450 int res
= fhandler_base::open (flags
| O_DIROPEN
, mode
);
1453 /* The file info in pc is wrong at this point for newly created files.
1454 Refresh it before fetching any file info. */
1456 pc
.get_finfo (get_io_handle ());
1458 if (pc
.isgood_inode (pc
.get_ino ()))
1459 ino
= pc
.get_ino ();
1462 syscall_printf ("%d = fhandler_disk_file::open(%S, %y)", res
,
1463 pc
.get_nt_native_path (), flags
);
1467 /* POSIX demands that pread/pwrite don't change the current file position.
1468 While NtReadFile/NtWriteFile support atomic seek-and-io, both change the
1469 file pointer if the file handle has been opened for synchonous I/O.
1470 Using this handle for pread/pwrite would break atomicity, because the
1471 read/write operation would have to be followed by a seek back to the old
1472 file position. What we do is to open another handle to the file on the
1473 first call to either pread or pwrite. This is used for any subsequent
1474 pread/pwrite. Thus the current file position of the "normal" file
1475 handle is not touched.
1479 Note that this is just a hack. The problem with this approach is that
1480 a change to the file permissions might disallow to open the file with
1481 the required permissions to read or write. This appears to be a border case,
1482 but that's exactly what git does. It creates the file for reading and
1483 writing and after writing it, it chmods the file to read-only. Then it
1484 calls pread on the file to examine the content. This works, but if git
1485 would use the original handle to pwrite to the file, it would be broken
1488 One way to implement this is to open the pread/pwrite handle right at
1489 file open time. We would simply maintain two handles, which wouldn't
1490 be much of a problem given how we do that for other fhandler types as
1493 However, ultimately fhandler_disk_file should become a derived class of
1494 fhandler_base_overlapped. Each raw_read or raw_write would fetch the
1495 actual file position, read/write from there, and then set the file
1496 position again. Fortunately, while the file position is not maintained
1497 by the I/O manager, it can be fetched and set to a new value by all
1498 processes holding a handle to that file object. Pread and pwrite differ
1499 from raw_read and raw_write just by not touching the current file pos.
1500 Actually they could be merged with raw_read/raw_write if we add a position
1501 parameter to the latter. */
1504 fhandler_disk_file::prw_open (bool write
)
1508 OBJECT_ATTRIBUTES attr
;
1510 /* First try to open with the original access mask */
1511 ACCESS_MASK access
= get_access ();
1512 status
= NtOpenFile (&prw_handle
, access
,
1513 pc
.init_reopen_attr (attr
, get_handle ()), &io
,
1514 FILE_SHARE_VALID_FLAGS
, get_options ());
1515 if (status
== STATUS_ACCESS_DENIED
)
1517 /* If we get an access denied, chmod has been called. Try again
1518 with just the required rights to perform the called function. */
1519 access
&= write
? ~GENERIC_READ
: ~GENERIC_WRITE
;
1520 status
= NtOpenFile (&prw_handle
, access
, &attr
, &io
,
1521 FILE_SHARE_VALID_FLAGS
, get_options ());
1523 debug_printf ("%y = NtOpenFile (%p, %y, %S, io, %y, %y)",
1524 status
, prw_handle
, access
, pc
.get_nt_native_path (),
1525 FILE_SHARE_VALID_FLAGS
, get_options ());
1526 if (!NT_SUCCESS (status
))
1528 __seterrno_from_nt_status (status
);
1535 fhandler_disk_file::pread (void *buf
, size_t count
, off_t offset
)
1539 if ((get_flags () & O_ACCMODE
) == O_WRONLY
)
1545 /* In binary mode, we can use an atomic NtReadFile call.
1546 Windows mandatory locking semantics disallow to use another HANDLE. */
1547 if (rbinary () && !mandatory_locking ())
1549 extern int __stdcall
is_at_eof (HANDLE h
);
1552 LARGE_INTEGER off
= { QuadPart
:offset
};
1554 if (!prw_handle
&& prw_open (false))
1556 status
= NtReadFile (prw_handle
, NULL
, NULL
, NULL
, &io
, buf
, count
,
1558 if (status
== STATUS_END_OF_FILE
)
1560 else if (!NT_SUCCESS (status
))
1567 if (status
== (NTSTATUS
) STATUS_ACCESS_VIOLATION
)
1569 if (is_at_eof (prw_handle
))
1574 switch (mmap_is_attached_or_noreserve (buf
, count
))
1576 case MMAP_NORESERVE_COMMITED
:
1577 status
= NtReadFile (prw_handle
, NULL
, NULL
, NULL
, &io
,
1578 buf
, count
, &off
, NULL
);
1579 if (NT_SUCCESS (status
))
1581 res
= io
.Information
;
1585 case MMAP_RAISE_SIGBUS
:
1591 __seterrno_from_nt_status (status
);
1595 res
= io
.Information
;
1600 /* Text mode stays slow and non-atomic. */
1601 off_t curpos
= lseek (0, SEEK_CUR
);
1602 if (curpos
< 0 || lseek (offset
, SEEK_SET
) < 0)
1606 size_t tmp_count
= count
;
1607 read (buf
, tmp_count
);
1608 if (lseek (curpos
, SEEK_SET
) >= 0)
1609 res
= (ssize_t
) tmp_count
;
1615 debug_printf ("%d = pread(%p, %ld, %D)\n", res
, buf
, count
, offset
);
1620 fhandler_disk_file::pwrite (void *buf
, size_t count
, off_t offset
)
1622 if ((get_flags () & O_ACCMODE
) == O_RDONLY
)
1628 /* In binary mode, we can use an atomic NtWriteFile call.
1629 Windows mandatory locking semantics disallow to use another HANDLE. */
1630 if (wbinary () && !mandatory_locking ())
1634 LARGE_INTEGER off
= { QuadPart
:offset
};
1636 if (!prw_handle
&& prw_open (true))
1638 status
= NtWriteFile (prw_handle
, NULL
, NULL
, NULL
, &io
, buf
, count
,
1640 if (!NT_SUCCESS (status
))
1642 __seterrno_from_nt_status (status
);
1645 return io
.Information
;
1649 /* Text mode stays slow and non-atomic. */
1651 off_t curpos
= lseek (0, SEEK_CUR
);
1652 if (curpos
< 0 || lseek (offset
, SEEK_SET
) < 0)
1656 res
= (ssize_t
) write (buf
, count
);
1657 if (lseek (curpos
, SEEK_SET
) < 0)
1660 debug_printf ("%d = pwrite(%p, %ld, %D)\n", res
, buf
, count
, offset
);
1665 fhandler_disk_file::mkdir (mode_t mode
)
1668 SECURITY_ATTRIBUTES sa
= sec_none_nih
;
1671 OBJECT_ATTRIBUTES attr
;
1673 PFILE_FULL_EA_INFORMATION p
= NULL
;
1675 ULONG access
= FILE_LIST_DIRECTORY
| SYNCHRONIZE
;
1677 if (pc
.fs_is_nfs ())
1679 /* When creating a dir on an NFS share, we have to set the
1680 file mode by writing a NFS fattr3 structure with the
1681 correct mode bits set. */
1682 plen
= sizeof (FILE_FULL_EA_INFORMATION
) + sizeof (NFS_V3_ATTR
)
1684 p
= (PFILE_FULL_EA_INFORMATION
) alloca (plen
);
1685 p
->NextEntryOffset
= 0;
1687 p
->EaNameLength
= sizeof (NFS_V3_ATTR
) - 1;
1688 p
->EaValueLength
= sizeof (fattr3
);
1689 strcpy (p
->EaName
, NFS_V3_ATTR
);
1690 fattr3
*nfs_attr
= (fattr3
*) (p
->EaName
+ p
->EaNameLength
+ 1);
1691 memset (nfs_attr
, 0, sizeof (fattr3
));
1692 nfs_attr
->type
= NF3DIR
;
1693 nfs_attr
->mode
= (mode
& 07777) & ~cygheap
->umask
;
1695 else if (has_acls () && !isremote ())
1696 /* If the filesystem supports ACLs, we will overwrite the DACL after the
1697 call to NtCreateFile. This requires a handle with READ_CONTROL and
1698 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
1699 open the file again.
1700 FIXME: On remote NTFS shares open sometimes fails because even the
1701 creator of the file doesn't have the right to change the DACL.
1702 I don't know what setting that is or how to recognize such a share,
1703 so for now we don't request WRITE_DAC on remote drives. */
1704 access
|= READ_CONTROL
| WRITE_DAC
;
1705 status
= NtCreateFile (&dir
, access
, pc
.get_object_attr (attr
, sa
), &io
, NULL
,
1706 FILE_ATTRIBUTE_DIRECTORY
, FILE_SHARE_VALID_FLAGS
,
1708 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
1709 | FILE_OPEN_FOR_BACKUP_INTENT
,
1711 if (NT_SUCCESS (status
))
1713 /* Set the "directory attribute" so that pc.isdir() returns correct
1714 value in subsequent function calls. */
1715 pc
.file_attributes (FILE_ATTRIBUTE_DIRECTORY
);
1717 set_created_file_access (dir
, pc
, mode
& 07777);
1722 __seterrno_from_nt_status (status
);
1728 fhandler_disk_file::rmdir ()
1730 extern NTSTATUS
unlink_nt (path_conv
&pc
);
1734 set_errno (ENOTDIR
);
1743 NTSTATUS status
= unlink_nt (pc
);
1745 /* Check for existence of remote dirs after trying to delete them.
1747 - Sometimes SMB indicates failure when it really succeeds.
1748 - Removing a directory on a Samba drive using an old Samba version
1749 sometimes doesn't return an error, if the directory can't be removed
1750 because it's not empty. */
1753 OBJECT_ATTRIBUTES attr
;
1754 FILE_BASIC_INFORMATION fbi
;
1757 q_status
= NtQueryAttributesFile (pc
.get_object_attr (attr
, sec_none_nih
),
1759 if (!NT_SUCCESS (status
) && q_status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1760 status
= STATUS_SUCCESS
;
1761 else if (pc
.fs_is_samba ()
1762 && NT_SUCCESS (status
) && NT_SUCCESS (q_status
))
1763 status
= STATUS_DIRECTORY_NOT_EMPTY
;
1765 if (!NT_SUCCESS (status
))
1767 __seterrno_from_nt_status (status
);
1773 /* This is the minimal number of entries which fit into the readdir cache.
1774 The number of bytes allocated by the cache is determined by this number,
1775 To tune caching, just tweak this number. To get a feeling for the size,
1776 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1778 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1780 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1781 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1782 + (NAME_MAX + 1) * sizeof (WCHAR)))
1786 char __cache
[DIR_BUF_SIZE
];
1790 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1791 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1793 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1796 fhandler_disk_file::opendir (int fd
)
1802 set_errno (ENOTDIR
);
1803 else if ((dir
= (DIR *) malloc (sizeof (DIR))) == NULL
)
1805 else if ((dir
->__d_dirname
= (char *) malloc ( sizeof (struct __DIR_cache
)))
1811 else if ((dir
->__d_dirent
=
1812 (struct dirent
*) malloc (sizeof (struct dirent
))) == NULL
)
1820 if (cfd
< 0 && fd
< 0)
1823 dir
->__d_dirent
->__d_version
= __DIRENT_VERSION
;
1824 dir
->__d_cookie
= __DIRENT_COOKIE
;
1825 dir
->__handle
= INVALID_HANDLE_VALUE
;
1826 dir
->__d_position
= 0;
1827 dir
->__flags
= (get_name ()[0] == '/' && get_name ()[1] == '\0')
1828 ? dirent_isroot
: 0;
1829 dir
->__d_internal
= 0;
1831 if (pc
.iscygdrive ())
1833 if (fd
< 0 && !open (O_RDONLY
, 0))
1838 dir
->__d_internal
= (uintptr_t) new __DIR_mounts (get_name ());
1839 d_cachepos (dir
) = 0;
1842 /* opendir() case. Initialize with given directory name and
1843 NULL directory handle. */
1844 OBJECT_ATTRIBUTES attr
;
1847 /* Tools like ls(1) call dirfd() to fetch the directory
1848 descriptor for calls to facl or fstat. The tight access mask
1849 used so far is not sufficient to reuse the handle for these
1850 calls, instead the facl/fstat calls find the handle to be
1851 unusable and have to re-open the file for reading attributes
1852 and control data. So, what we do here is to try to open the
1853 directory with more relaxed access mask which enables to use
1854 the handle for the aforementioned purpose. This should work
1855 in almost all cases. Only if it doesn't work due to
1856 permission problems, we drop the additional access bits and
1858 ACCESS_MASK fstat_mask
= READ_CONTROL
| FILE_READ_ATTRIBUTES
;
1862 status
= NtOpenFile (&get_handle (),
1863 SYNCHRONIZE
| FILE_LIST_DIRECTORY
1865 pc
.get_object_attr (attr
, sec_none_nih
),
1866 &io
, FILE_SHARE_VALID_FLAGS
,
1867 FILE_SYNCHRONOUS_IO_NONALERT
1868 | FILE_OPEN_FOR_BACKUP_INTENT
1869 | FILE_DIRECTORY_FILE
);
1870 if (!NT_SUCCESS (status
))
1872 if (status
== STATUS_ACCESS_DENIED
&& fstat_mask
)
1876 __seterrno_from_nt_status (status
);
1881 while (!NT_SUCCESS (status
));
1884 /* FileIdBothDirectoryInformation was unsupported on XP when
1885 accessing UDF. It's not clear if the call isn't also unsupported
1886 on other OS/FS combinations. Instead of testing for yet another
1887 error code, use FileIdBothDirectoryInformation only on FSes
1888 supporting persistent ACLs.
1890 NFS clients hide dangling symlinks from directory queries,
1891 unless you use the FileNamesInformation info class.
1892 FileIdBothDirectoryInformation works fine, but only if the NFS
1893 share is mounted to a drive letter. TODO: We don't test that
1894 here for now, but it might be worth to test if there's a speed
1895 gain in using FileIdBothDirectoryInformation, because it doesn't
1896 require to open the file to read the inode number. */
1897 if (pc
.hasgood_inode ())
1899 dir
->__flags
|= dirent_set_d_ino
;
1900 if (pc
.fs_is_nfs ())
1901 dir
->__flags
|= dirent_nfs_d_ino
;
1902 else if (!pc
.has_buggy_fileid_dirinfo ())
1903 dir
->__flags
|= dirent_get_d_ino
;
1910 /* Filling cfd with `this' (aka storing this in the file
1911 descriptor table should only happen after it's clear that
1912 opendir doesn't fail, otherwise we end up cfree'ing the
1913 fhandler twice, once in opendir() in dir.cc, the second
1914 time on exit. Nasty, nasty... */
1918 set_close_on_exec (true);
1923 syscall_printf ("%p = opendir (%s)", res
, get_name ());
1927 delete d_mounts (dir
);
1929 free (dir
->__d_dirent
);
1931 free (dir
->__d_dirname
);
1938 readdir_get_ino (const char *path
, bool dot_dot
)
1943 OBJECT_ATTRIBUTES attr
;
1949 fname
= (char *) alloca (strlen (path
) + 4);
1950 char *c
= stpcpy (fname
, path
);
1956 path_conv
pc (path
, PC_SYM_NOFOLLOW
| PC_POSIX
| PC_NOWARN
| PC_KEEP_HANDLE
);
1957 if (pc
.isspecial ())
1959 if (!stat_worker (pc
, &st
))
1962 else if (!pc
.hasgood_inode ())
1963 ino
= hash_path_name (0, pc
.get_nt_native_path ());
1964 else if ((hdl
= pc
.handle ()) != NULL
1965 || NT_SUCCESS (NtOpenFile (&hdl
, READ_CONTROL
,
1966 pc
.get_object_attr (attr
, sec_none_nih
),
1967 &io
, FILE_SHARE_VALID_FLAGS
,
1968 FILE_OPEN_FOR_BACKUP_INTENT
1969 | (pc
.is_rep_symlink ()
1970 ? FILE_OPEN_REPARSE_POINT
: 0)))
1973 ino
= pc
.get_ino_by_handle (hdl
);
1975 ino
= hash_path_name (0, pc
.get_nt_native_path ());
1981 fhandler_disk_file::readdir_helper (DIR *dir
, dirent
*de
, DWORD w32_err
,
1982 DWORD attr
, PUNICODE_STRING fname
)
1986 switch (d_mounts (dir
)->check_missing_mount (fname
))
1988 case __DIR_mount_none
:
1990 return geterrno_from_win_error (w32_err
);
1991 case __DIR_mount_virt_target
:
1992 de
->d_type
= DT_DIR
;
1998 dir
->__flags
&= ~dirent_set_d_ino
;
2001 /* Set d_type if type can be determined from file attributes. For .lnk
2002 symlinks, d_type will be reset below. Reparse points can be NTFS
2003 symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
2004 if (attr
&& !(attr
& ~FILE_ATTRIBUTE_VALID_FLAGS
))
2006 if (attr
& FILE_ATTRIBUTE_DIRECTORY
)
2007 de
->d_type
= DT_DIR
;
2008 /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
2009 else if (!(attr
& FILE_ATTRIBUTE_SYSTEM
))
2010 de
->d_type
= DT_REG
;
2013 /* Check for reparse points that can be treated as posix symlinks.
2014 Mountpoints and unknown or unhandled reparse points will be treated
2015 as normal file/directory/unknown. In all cases, returning the INO of
2016 the reparse point (not of the target) matches behavior of posix systems.
2018 if (attr
& FILE_ATTRIBUTE_REPARSE_POINT
)
2020 OBJECT_ATTRIBUTES oattr
;
2022 InitializeObjectAttributes (&oattr
, fname
, pc
.objcaseinsensitive (),
2023 get_handle (), NULL
);
2024 if (readdir_check_reparse_point (&oattr
, isremote ()))
2025 de
->d_type
= DT_LNK
;
2028 /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
2029 .lnk suffix and set d_type accordingly. */
2030 if ((attr
& (FILE_ATTRIBUTE_DIRECTORY
2031 | FILE_ATTRIBUTE_REPARSE_POINT
2032 | FILE_ATTRIBUTE_READONLY
)) == FILE_ATTRIBUTE_READONLY
2033 && fname
->Length
> 4 * sizeof (WCHAR
))
2035 UNICODE_STRING uname
;
2037 RtlInitCountedUnicodeString (&uname
,
2039 + fname
->Length
/ sizeof (WCHAR
) - 4,
2040 4 * sizeof (WCHAR
));
2041 if (RtlEqualUnicodeString (&uname
, &ro_u_lnk
, TRUE
))
2044 char *file
= tp
.c_get ();
2045 char *p
= stpcpy (file
, pc
.get_posix ());
2048 sys_wcstombs (p
, NT_MAX_PATH
- (p
- file
),
2049 fname
->Buffer
, fname
->Length
/ sizeof (WCHAR
));
2050 path_conv
fpath (file
, PC_SYM_NOFOLLOW
);
2051 if (fpath
.issymlink ())
2053 fname
->Length
-= 4 * sizeof (WCHAR
);
2054 de
->d_type
= DT_LNK
;
2056 else if (fpath
.isfifo ())
2058 fname
->Length
-= 4 * sizeof (WCHAR
);
2059 de
->d_type
= DT_FIFO
;
2061 else if (fpath
.is_fs_special ())
2063 fname
->Length
-= 4 * sizeof (WCHAR
);
2064 de
->d_type
= S_ISCHR (fpath
.dev
.mode ()) ? DT_CHR
: DT_BLK
;
2069 sys_wcstombs (de
->d_name
, NAME_MAX
+ 1, fname
->Buffer
,
2070 fname
->Length
/ sizeof (WCHAR
));
2072 /* Don't try to optimize relative to dir->__d_position. On several
2073 filesystems it's no safe bet that "." and ".." entries always
2075 if (de
->d_name
[0] == '.')
2077 if (de
->d_name
[1] == '\0')
2078 dir
->__flags
|= dirent_saw_dot
;
2079 else if (de
->d_name
[1] == '.' && de
->d_name
[2] == '\0')
2080 dir
->__flags
|= dirent_saw_dot_dot
;
2086 fhandler_disk_file::readdir (DIR *dir
, dirent
*de
)
2089 NTSTATUS status
= STATUS_SUCCESS
;
2090 PFILE_ID_BOTH_DIR_INFORMATION buf
= NULL
;
2092 ULONG FileNameLength
;
2093 ULONG FileAttributes
;
2095 UNICODE_STRING fname
;
2097 /* d_cachepos always refers to the next cache entry to use. If it's 0
2098 we must reload the cache. */
2101 if (d_cachepos (dir
) == 0)
2103 if ((dir
->__flags
& dirent_get_d_ino
))
2105 status
= NtQueryDirectoryFile (get_handle (), NULL
, NULL
, NULL
, &io
,
2106 d_cache (dir
), DIR_BUF_SIZE
,
2107 FileIdBothDirectoryInformation
,
2108 FALSE
, NULL
, dir
->__d_position
== 0);
2109 /* FileIdBothDirectoryInformation isn't supported for remote drives
2110 on NT4 and 2K systems. There are also hacked versions of
2111 Samba 3.0.x out there (Debian-based it seems), which return
2112 STATUS_NOT_SUPPORTED rather than handling this info class.
2113 We just fall back to using a standard directory query in
2114 this case and note this case using the dirent_get_d_ino flag. */
2115 if (!NT_SUCCESS (status
) && status
!= STATUS_NO_MORE_FILES
2116 && (status
== STATUS_INVALID_LEVEL
2117 || status
== STATUS_NOT_SUPPORTED
2118 || status
== STATUS_INVALID_PARAMETER
2119 || status
== STATUS_INVALID_NETWORK_RESPONSE
2120 || status
== STATUS_INVALID_INFO_CLASS
))
2121 dir
->__flags
&= ~dirent_get_d_ino
;
2122 /* Something weird happens on Samba up to version 3.0.21c, which is
2123 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
2124 nicely, but only up to the 128th entry in the directory. After
2125 reaching this entry, the next call to NtQueryDirectoryFile
2126 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
2127 Why should we care, we can just switch to
2128 FileBothDirectoryInformation, isn't it? Nope! The next call to
2129 NtQueryDirectoryFile(FileBothDirectoryInformation) actually
2130 returns STATUS_NO_MORE_FILES, regardless how many files are left
2131 unread in the directory. This does not happen when using
2132 FileBothDirectoryInformation right from the start, but since
2133 we can't decide whether the server we're talking with has this
2134 bug or not, we end up serving Samba shares always in the slow
2135 mode using FileBothDirectoryInformation. So, what we do here is
2136 to implement the solution suggested by Andrew Tridgell, we just
2137 reread all entries up to dir->d_position using
2138 FileBothDirectoryInformation.
2139 However, We do *not* mark this server as broken and fall back to
2140 using FileBothDirectoryInformation further on. This would slow
2141 down every access to such a server, even for directories under
2142 128 entries. Also, bigger dirs only suffer from one additional
2143 call per full directory scan, which shouldn't be too big a hit.
2144 This can easily be changed if necessary. */
2145 if (status
== STATUS_INVALID_LEVEL
&& dir
->__d_position
)
2147 d_cachepos (dir
) = 0;
2148 for (int cnt
= 0; cnt
< dir
->__d_position
; ++cnt
)
2150 if (d_cachepos (dir
) == 0)
2152 status
= NtQueryDirectoryFile (get_handle (), NULL
, NULL
,
2153 NULL
, &io
, d_cache (dir
),
2155 FileBothDirectoryInformation
,
2156 FALSE
, NULL
, cnt
== 0);
2157 if (!NT_SUCCESS (status
))
2160 buf
= (PFILE_ID_BOTH_DIR_INFORMATION
) (d_cache (dir
)
2161 + d_cachepos (dir
));
2162 if (buf
->NextEntryOffset
== 0)
2163 d_cachepos (dir
) = 0;
2165 d_cachepos (dir
) += buf
->NextEntryOffset
;
2170 if (!(dir
->__flags
& dirent_get_d_ino
))
2171 status
= NtQueryDirectoryFile (get_handle (), NULL
, NULL
, NULL
, &io
,
2172 d_cache (dir
), DIR_BUF_SIZE
,
2173 (dir
->__flags
& dirent_nfs_d_ino
)
2174 ? FileNamesInformation
2175 : FileBothDirectoryInformation
,
2176 FALSE
, NULL
, dir
->__d_position
== 0);
2181 if (status
== STATUS_NO_MORE_FILES
)
2183 else if (!NT_SUCCESS (status
))
2184 debug_printf ("NtQueryDirectoryFile failed, status %y, win32 error %u",
2185 status
, RtlNtStatusToDosError (status
));
2188 buf
= (PFILE_ID_BOTH_DIR_INFORMATION
) (d_cache (dir
) + d_cachepos (dir
));
2189 if (buf
->NextEntryOffset
== 0)
2190 d_cachepos (dir
) = 0;
2192 d_cachepos (dir
) += buf
->NextEntryOffset
;
2193 if ((dir
->__flags
& dirent_get_d_ino
))
2195 FileName
= buf
->FileName
;
2196 FileNameLength
= buf
->FileNameLength
;
2197 FileAttributes
= buf
->FileAttributes
;
2198 if ((dir
->__flags
& dirent_set_d_ino
))
2199 de
->d_ino
= buf
->FileId
.QuadPart
;
2201 else if ((dir
->__flags
& dirent_nfs_d_ino
))
2203 FileName
= ((PFILE_NAMES_INFORMATION
) buf
)->FileName
;
2204 FileNameLength
= ((PFILE_NAMES_INFORMATION
) buf
)->FileNameLength
;
2208 FileName
= ((PFILE_BOTH_DIR_INFORMATION
) buf
)->FileName
;
2210 ((PFILE_BOTH_DIR_INFORMATION
) buf
)->FileNameLength
;
2212 ((PFILE_BOTH_DIR_INFORMATION
) buf
)->FileAttributes
;
2214 /* We don't show O_TMPFILE files in the filesystem. This is a kludge,
2215 so we may end up removing this snippet again. */
2216 if (FileAttributes
& FILE_ATTRIBUTE_TEMPORARY
)
2218 RtlInitCountedUnicodeString (&fname
, FileName
, FileNameLength
);
2219 d_mounts (dir
)->check_mount (&fname
);
2220 if (de
->d_ino
== 0 && (dir
->__flags
& dirent_set_d_ino
))
2222 /* Don't try to optimize relative to dir->__d_position. On several
2223 filesystems it's no safe bet that "." and ".." entries always
2225 if (FileNameLength
== sizeof (WCHAR
) && FileName
[0] == '.')
2226 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2227 else if (FileNameLength
== 2 * sizeof (WCHAR
)
2228 && FileName
[0] == L
'.' && FileName
[1] == L
'.')
2230 if (!(dir
->__flags
& dirent_isroot
))
2231 de
->d_ino
= readdir_get_ino (get_name (), true);
2233 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2237 OBJECT_ATTRIBUTES attr
;
2241 InitializeObjectAttributes (&attr
, &fname
,
2242 pc
.objcaseinsensitive (),
2243 get_handle (), NULL
);
2244 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2245 NtOpenFile here returns the inode number of the symlink target,
2246 rather than the inode number of the symlink itself.
2248 Worse, trying to open a symlink without setting the special
2249 "ActOnSymlink" EA triggers a bug in Windows 7 which results
2250 in a timeout of up to 20 seconds, followed by two exceptions
2253 Since both results are far from desirable, we open symlinks
2254 on NFS so that we get the right inode and a happy W7.
2255 And, since some filesystems choke on the EAs, we don't
2256 use them unconditionally. */
2257 f_status
= (dir
->__flags
& dirent_nfs_d_ino
)
2258 ? NtCreateFile (&hdl
, READ_CONTROL
, &attr
, &io
,
2259 NULL
, 0, FILE_SHARE_VALID_FLAGS
,
2260 FILE_OPEN
, FILE_OPEN_FOR_BACKUP_INTENT
,
2261 &nfs_aol_ffei
, sizeof nfs_aol_ffei
)
2262 : NtOpenFile (&hdl
, READ_CONTROL
, &attr
, &io
,
2263 FILE_SHARE_VALID_FLAGS
,
2264 FILE_OPEN_FOR_BACKUP_INTENT
2265 | FILE_OPEN_REPARSE_POINT
);
2266 if (NT_SUCCESS (f_status
))
2268 /* We call NtQueryInformationFile here, rather than
2269 pc.get_ino_by_handle(), otherwise we can't short-circuit
2270 dirent_set_d_ino correctly. */
2271 FILE_INTERNAL_INFORMATION fii
;
2272 f_status
= NtQueryInformationFile (hdl
, &io
, &fii
, sizeof fii
,
2273 FileInternalInformation
);
2275 if (NT_SUCCESS (f_status
))
2277 if (pc
.isgood_inode (fii
.IndexNumber
.QuadPart
))
2278 de
->d_ino
= fii
.IndexNumber
.QuadPart
;
2280 /* Untrusted file system. Don't try to fetch inode
2282 dir
->__flags
&= ~dirent_set_d_ino
;
2289 if (!(res
= readdir_helper (dir
, de
, RtlNtStatusToDosError (status
),
2290 FileAttributes
, &fname
)))
2291 dir
->__d_position
++;
2292 else if (!(dir
->__flags
& dirent_saw_dot
))
2294 strcpy (de
->d_name
, ".");
2295 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2296 de
->d_type
= DT_DIR
;
2297 dir
->__d_position
++;
2298 dir
->__flags
|= dirent_saw_dot
;
2301 else if (!(dir
->__flags
& dirent_saw_dot_dot
))
2303 strcpy (de
->d_name
, "..");
2304 if (!(dir
->__flags
& dirent_isroot
))
2305 de
->d_ino
= readdir_get_ino (get_name (), true);
2307 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2308 de
->d_type
= DT_DIR
;
2309 dir
->__d_position
++;
2310 dir
->__flags
|= dirent_saw_dot_dot
;
2314 syscall_printf ("%d = readdir(%p, %p) (L\"%lS\" > \"%ls\") (attr %y > type %d)",
2315 res
, dir
, &de
, res
? NULL
: &fname
, res
? "***" : de
->d_name
,
2316 FileAttributes
, de
->d_type
);
2321 fhandler_disk_file::telldir (DIR *dir
)
2323 return dir
->__d_position
;
2327 fhandler_disk_file::seekdir (DIR *dir
, long loc
)
2330 while (loc
> dir
->__d_position
)
2331 if (!::readdir (dir
))
2336 fhandler_disk_file::rewinddir (DIR *dir
)
2338 d_cachepos (dir
) = 0;
2339 dir
->__d_position
= 0;
2340 d_mounts (dir
)->rewind ();
2344 fhandler_disk_file::closedir (DIR *dir
)
2348 delete d_mounts (dir
);
2349 syscall_printf ("%d = closedir(%p, %s)", res
, dir
, get_name ());
2353 fhandler_cygdrive::fhandler_cygdrive () :
2354 fhandler_disk_file (), ndrives (0), pdrive (NULL
)
2359 fhandler_cygdrive::open (int flags
, mode_t mode
)
2361 if ((flags
& (O_CREAT
| O_EXCL
)) == (O_CREAT
| O_EXCL
))
2366 if (flags
& O_WRONLY
)
2371 /* Open a fake handle to \\Device\\Null */
2372 return open_null (flags
);
2376 fhandler_cygdrive::set_drives ()
2378 pdrive
= pdrive_buf
;
2379 ndrives
= GetLogicalDriveStrings (sizeof pdrive_buf
, pdrive_buf
) / DRVSZ
;
2383 fhandler_cygdrive::fstat (struct stat
*buf
)
2385 fhandler_base::fstat (buf
);
2387 buf
->st_mode
= S_IFDIR
| STD_RBITS
| STD_XBITS
;
2393 fhandler_cygdrive::fstatvfs (struct statvfs
*sfs
)
2395 /* Virtual file system. Just return an empty buffer with a few values
2396 set to something useful. Just as on Linux. */
2397 memset (sfs
, 0, sizeof (*sfs
));
2398 sfs
->f_bsize
= sfs
->f_frsize
= 4096;
2399 sfs
->f_flag
= ST_RDONLY
;
2400 sfs
->f_namemax
= NAME_MAX
;
2405 fhandler_cygdrive::opendir (int fd
)
2409 dir
= fhandler_disk_file::opendir (fd
);
2410 if (dir
&& !ndrives
)
2417 fhandler_cygdrive::readdir (DIR *dir
, dirent
*de
)
2419 WCHAR drive
[] = L
"X:";
2423 if (!pdrive
|| !*pdrive
)
2425 if (!(dir
->__flags
& dirent_saw_dot
))
2427 de
->d_name
[0] = '.';
2428 de
->d_name
[1] = '\0';
2433 disk_type dt
= get_disk_type ((drive
[0] = *pdrive
, drive
));
2434 if (dt
== DT_SHARE_SMB
)
2436 /* Calling NetUseGetInfo on SMB drives allows to fetch the
2437 current state of the drive without trying to open a file
2438 descriptor on the share (GetFileAttributes). This avoids
2439 waiting for SMB timeouts. Of course, there's a downside:
2440 If a drive becomes availabe again, it can take a couple of
2441 minutes to recognize it. As long as this didn't happen,
2442 the drive will not show up in the cygdrive dir. */
2446 if (NetUseGetInfo (NULL
, drive
, 1, (PBYTE
*) &pui1
) == NERR_Success
)
2448 status
= pui1
->ui1_status
;
2449 NetApiBufferFree (pui1
);
2450 if (status
== USE_OK
)
2454 else if (dt
!= DT_FLOPPY
2455 && GetFileAttributes (pdrive
) != INVALID_FILE_ATTRIBUTES
)
2457 pdrive
= strchr (pdrive
, '\0') + 1;
2459 *de
->d_name
= cyg_tolower (*pdrive
);
2460 de
->d_name
[1] = '\0';
2461 user_shared
->warned_msdos
= true;
2462 de
->d_ino
= readdir_get_ino (pdrive
, false);
2463 dir
->__d_position
++;
2464 pdrive
= strchr (pdrive
, '\0') + 1;
2465 syscall_printf ("%p = readdir (%p) (%s)", &de
, dir
, de
->d_name
);
2470 fhandler_cygdrive::rewinddir (DIR *dir
)
2472 pdrive
= pdrive_buf
;
2473 dir
->__d_position
= 0;
2477 fhandler_cygdrive::closedir (DIR *dir
)
2479 pdrive
= pdrive_buf
;