1 /* fhandler_disk_file.cc
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008, 2009, 2010 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
15 #include <sys/statvfs.h>
22 #include "shared_info.h"
29 #define _COMPILING_NEWLIB
35 const char *parent_dir
;
37 UNICODE_STRING mounts
[MAX_MOUNTS
];
38 bool found
[MAX_MOUNTS
+ 2];
39 UNICODE_STRING cygdrive
;
41 #define __DIR_PROC (MAX_MOUNTS)
42 #define __DIR_CYGDRIVE (MAX_MOUNTS+1)
44 __ino64_t
eval_ino (int idx
)
47 char fname
[parent_dir_len
+ mounts
[idx
].Length
+ 2];
50 char *c
= stpcpy (fname
, parent_dir
);
53 sys_wcstombs (c
, mounts
[idx
].Length
+ 1,
54 mounts
[idx
].Buffer
, mounts
[idx
].Length
/ sizeof (WCHAR
));
55 path_conv
pc (fname
, PC_SYM_NOFOLLOW
| PC_POSIX
);
56 if (!stat_worker (pc
, &st
))
62 __DIR_mounts (const char *posix_path
)
63 : parent_dir (posix_path
)
65 parent_dir_len
= strlen (parent_dir
);
66 count
= mount_table
->get_mounts_here (parent_dir
, parent_dir_len
, mounts
,
72 for (int i
= 0; i
< count
; ++i
)
73 RtlFreeUnicodeString (&mounts
[i
]);
74 RtlFreeUnicodeString (&cygdrive
);
76 __ino64_t
check_mount (PUNICODE_STRING fname
, __ino64_t ino
,
79 if (parent_dir_len
== 1) /* root dir */
81 if (RtlEqualUnicodeString (fname
, &ro_u_proc
, FALSE
))
83 found
[__DIR_PROC
] = true;
86 if (fname
->Length
/ sizeof (WCHAR
) == mount_table
->cygdrive_len
- 2
87 && RtlEqualUnicodeString (fname
, &cygdrive
, FALSE
))
89 found
[__DIR_CYGDRIVE
] = true;
93 for (int i
= 0; i
< count
; ++i
)
94 if (RtlEqualUnicodeString (fname
, &mounts
[i
], FALSE
))
97 return eval
? eval_ino (i
) : 1;
101 __ino64_t
check_missing_mount (PUNICODE_STRING retname
= NULL
)
103 for (int i
= 0; i
< count
; ++i
)
109 *retname
= mounts
[i
];
114 if (parent_dir_len
== 1) /* root dir */
116 if (!found
[__DIR_PROC
])
118 found
[__DIR_PROC
] = true;
120 *retname
= ro_u_proc
;
123 if (!found
[__DIR_CYGDRIVE
])
125 found
[__DIR_CYGDRIVE
] = true;
126 if (cygdrive
.Length
> 0)
136 void rewind () { memset (found
, 0, sizeof found
); }
140 path_conv::isgood_inode (__ino64_t ino
) const
142 /* We can't trust remote inode numbers of only 32 bit. That means,
143 all remote inode numbers when running under NT4, as well as remote NT4
144 NTFS, as well as shares of Samba version < 3.0.
145 The known exception are SFU NFS shares, which return the valid 32 bit
146 inode number from the remote file system unchanged. */
147 return hasgood_inode () && (ino
> UINT32_MAX
|| !isremote () || fs_is_nfs ());
150 /* Check reparse point for type. IO_REPARSE_TAG_MOUNT_POINT types are
151 either volume mount points, which are treated as directories, or they
152 are directory mount points, which are treated as symlinks.
153 IO_REPARSE_TAG_SYMLINK types are always symlinks. We don't know
154 anything about other reparse points, so they are treated as unknown. */
156 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr
)
158 DWORD ret
= DT_UNKNOWN
;
161 UNICODE_STRING subst
;
163 if (NT_SUCCESS (NtOpenFile (&reph
, READ_CONTROL
, attr
, &io
,
164 FILE_SHARE_VALID_FLAGS
,
165 FILE_OPEN_FOR_BACKUP_INTENT
166 | FILE_OPEN_REPARSE_POINT
)))
168 PREPARSE_DATA_BUFFER rp
= (PREPARSE_DATA_BUFFER
)
169 alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE
);
170 if (NT_SUCCESS (NtFsControlFile (reph
, NULL
, NULL
, NULL
,
171 &io
, FSCTL_GET_REPARSE_POINT
, NULL
, 0,
172 (LPVOID
) rp
, MAXIMUM_REPARSE_DATA_BUFFER_SIZE
)))
174 if (rp
->ReparseTag
== IO_REPARSE_TAG_MOUNT_POINT
)
176 RtlInitCountedUnicodeString (&subst
,
177 (WCHAR
*)((char *)rp
->MountPointReparseBuffer
.PathBuffer
178 + rp
->MountPointReparseBuffer
.SubstituteNameOffset
),
179 rp
->MountPointReparseBuffer
.SubstituteNameLength
);
180 /* Only volume mountpoints are treated as directories. */
181 if (RtlEqualUnicodePathPrefix (&subst
, &ro_u_volume
, TRUE
))
186 else if (rp
->ReparseTag
== IO_REPARSE_TAG_SYMLINK
)
195 path_conv::get_ino_by_handle (HANDLE hdl
)
198 FILE_INTERNAL_INFORMATION fai
;
200 if (NT_SUCCESS (NtQueryInformationFile (hdl
, &io
, &fai
, sizeof fai
,
201 FileInternalInformation
))
202 && isgood_inode (fai
.FileId
.QuadPart
))
203 return fai
.FileId
.QuadPart
;
208 /* This function is obsolete. We're keeping it in so we don't forget
209 that we already did all that at one point. */
211 path_conv::ndisk_links (DWORD nNumberOfLinks
)
213 if (!isdir () || isremote ())
214 return nNumberOfLinks
;
216 OBJECT_ATTRIBUTES attr
;
220 if (!NT_SUCCESS (NtOpenFile (&fh
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
221 get_object_attr (attr
, sec_none_nih
),
222 &io
, FILE_SHARE_VALID_FLAGS
,
223 FILE_SYNCHRONOUS_IO_NONALERT
224 | FILE_OPEN_FOR_BACKUP_INTENT
225 | FILE_DIRECTORY_FILE
)))
226 return nNumberOfLinks
;
230 PFILE_BOTH_DIRECTORY_INFORMATION fdibuf
= (PFILE_BOTH_DIRECTORY_INFORMATION
)
232 __DIR_mounts
*dir
= new __DIR_mounts (normalized_path
);
233 while (NT_SUCCESS (NtQueryDirectoryFile (fh
, NULL
, NULL
, NULL
, &io
, fdibuf
,
234 65536, FileBothDirectoryInformation
,
235 FALSE
, NULL
, first
)))
240 /* All directories have . and .. as their first entries.
241 If . is not present as first entry, we're on a drive's
242 root direcotry, which doesn't have these entries. */
243 if (fdibuf
->FileNameLength
!= 2 || fdibuf
->FileName
[0] != L
'.')
246 for (PFILE_BOTH_DIRECTORY_INFORMATION pfdi
= fdibuf
;
248 pfdi
= (PFILE_BOTH_DIRECTORY_INFORMATION
)
249 (pfdi
->NextEntryOffset
? (PBYTE
) pfdi
+ pfdi
->NextEntryOffset
252 switch (pfdi
->FileAttributes
253 & (FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_REPARSE_POINT
))
255 case FILE_ATTRIBUTE_DIRECTORY
:
256 /* Just a directory */
259 case FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_REPARSE_POINT
:
260 /* Volume mount point or symlink to directory */
262 UNICODE_STRING fname
;
264 RtlInitCountedUnicodeString (&fname
, pfdi
->FileName
,
265 pfdi
->FileNameLength
);
266 InitializeObjectAttributes (&attr
, &fname
,
267 objcaseinsensitive (), fh
, NULL
);
268 if (is_volume_mountpoint (&attr
))
275 UNICODE_STRING fname
;
276 RtlInitCountedUnicodeString (&fname
, pfdi
->FileName
,
277 pfdi
->FileNameLength
);
278 dir
->check_mount (&fname
, 0, false);
281 while (dir
->check_missing_mount ())
289 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
290 This returns the content of a struct fattr3 as defined in RFC 1813.
291 The content is the NFS equivalent of struct stat. so there's not much
292 to do here except for copying. */
294 fhandler_base::fstat_by_nfs_ea (struct __stat64
*buf
)
296 fattr3
*nfs_attr
= pc
.nfsattr ();
298 if (get_io_handle ())
300 /* NFS stumbles over its own caching. If you write to the file,
301 a subsequent fstat does not return the actual size of the file,
302 but the size at the time the handle has been opened. Unless
303 access through another handle invalidates the caching within the
305 if (get_access () & GENERIC_WRITE
)
306 FlushFileBuffers (get_io_handle ());
307 nfs_fetch_fattr3 (get_io_handle (), nfs_attr
);
309 buf
->st_dev
= nfs_attr
->fsid
;
310 buf
->st_ino
= nfs_attr
->fileid
;
311 buf
->st_mode
= (nfs_attr
->mode
& 0xfff)
312 | nfs_type_mapping
[nfs_attr
->type
& 7];
313 buf
->st_nlink
= nfs_attr
->nlink
;
314 /* FIXME: How to convert UNIX uid/gid to Windows SIDs? */
316 buf
->st_uid
= nfs_attr
->uid
;
317 buf
->st_gid
= nfs_attr
->gid
;
319 buf
->st_uid
= myself
->uid
;
320 buf
->st_gid
= myself
->gid
;
322 buf
->st_rdev
= makedev (nfs_attr
->rdev
.specdata1
,
323 nfs_attr
->rdev
.specdata2
);
324 buf
->st_size
= nfs_attr
->size
;
325 buf
->st_blksize
= PREFERRED_IO_BLKSIZE
;
326 buf
->st_blocks
= nfs_attr
->used
/ 512;
327 buf
->st_atim
= nfs_attr
->atime
;
328 buf
->st_mtim
= nfs_attr
->mtime
;
329 buf
->st_ctim
= nfs_attr
->ctime
;
334 fhandler_base::fstat_by_handle (struct __stat64
*buf
)
336 /* Don't use FileAllInformation info class. It returns a pathname rather
337 than a filename, so it needs a really big buffer for no good reason
338 since we don't need the name anyway. So we just call the three info
339 classes necessary to get all information required by stat(2). */
340 FILE_STANDARD_INFORMATION fsi
;
341 FILE_INTERNAL_INFORMATION fii
;
343 HANDLE h
= get_stat_handle ();
347 /* If the file has been opened for other purposes than stat, we can't rely
348 on the information stored in pc.fnoi. So we overwrite them here. */
349 if (get_io_handle ())
351 PFILE_NETWORK_OPEN_INFORMATION pfnoi
= pc
.fnoi ();
352 status
= NtQueryInformationFile (h
, &io
, pfnoi
, sizeof *pfnoi
,
353 FileNetworkOpenInformation
);
354 if (!NT_SUCCESS (status
))
356 debug_printf ("%p = NtQueryInformationFile(%S, "
357 "FileNetworkOpenInformation)",
358 status
, pc
.get_nt_native_path ());
362 if (!pc
.hasgood_inode ())
363 fsi
.NumberOfLinks
= 1;
366 status
= NtQueryInformationFile (h
, &io
, &fsi
, sizeof fsi
,
367 FileStandardInformation
);
368 if (!NT_SUCCESS (status
))
370 debug_printf ("%p = NtQueryInformationFile(%S, "
371 "FileStandardInformation)",
372 status
, pc
.get_nt_native_path ());
377 status
= NtQueryInformationFile (h
, &io
, &fii
, sizeof fii
,
378 FileInternalInformation
);
379 if (!NT_SUCCESS (status
))
381 debug_printf ("%p = NtQueryInformationFile(%S, "
382 "FileInternalInformation)",
383 status
, pc
.get_nt_native_path ());
386 ino
= fii
.FileId
.QuadPart
;
389 return fstat_helper (buf
, fsi
.NumberOfLinks
);
393 fhandler_base::fstat_by_name (struct __stat64
*buf
)
396 OBJECT_ATTRIBUTES attr
;
398 UNICODE_STRING dirname
;
399 UNICODE_STRING basename
;
402 FILE_ID_BOTH_DIR_INFORMATION fdi
;
403 WCHAR buf
[NAME_MAX
+ 1];
406 if (!ino
&& pc
.hasgood_inode ()
407 && wincap
.has_fileid_dirinfo () && !pc
.has_buggy_fileid_dirinfo ())
409 RtlSplitUnicodePath (pc
.get_nt_native_path (), &dirname
, &basename
);
410 InitializeObjectAttributes (&attr
, &dirname
, pc
.objcaseinsensitive (),
412 status
= NtOpenFile (&dir
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
413 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
414 FILE_SYNCHRONOUS_IO_NONALERT
415 | FILE_OPEN_FOR_BACKUP_INTENT
416 | FILE_DIRECTORY_FILE
);
417 if (!NT_SUCCESS (status
))
418 debug_printf ("%p = NtOpenFile(%S)", status
,
419 pc
.get_nt_native_path ());
422 status
= NtQueryDirectoryFile (dir
, NULL
, NULL
, NULL
, &io
,
423 &fdi_buf
.fdi
, sizeof fdi_buf
,
424 FileIdBothDirectoryInformation
,
425 TRUE
, &basename
, TRUE
);
427 if (!NT_SUCCESS (status
))
428 debug_printf ("%p = NtQueryDirectoryFile(%S)", status
,
429 pc
.get_nt_native_path ());
431 ino
= fdi_buf
.fdi
.FileId
.QuadPart
;
434 return fstat_helper (buf
, 1);
438 fhandler_base::fstat_fs (struct __stat64
*buf
)
442 int open_flags
= O_RDONLY
| O_BINARY
;
444 if (get_stat_handle ())
446 if (!nohandle () && !is_fs_special ())
447 res
= pc
.fs_is_nfs () ? fstat_by_nfs_ea (buf
) : fstat_by_handle (buf
);
449 res
= fstat_by_name (buf
);
452 /* First try to open with generic read access. This allows to read the file
453 in fstat_helper (when checking for executability) without having to
454 re-open it. Opening a file can take a lot of time on network drives
455 so we try to avoid that. */
456 oret
= open_fs (open_flags
, 0);
459 query_open (query_read_attributes
);
460 oret
= open_fs (open_flags
, 0);
464 /* We now have a valid handle, regardless of the "nohandle" state.
465 Since fhandler_base::open only calls CloseHandle if !nohandle,
466 we have to set it to false before calling close and restore
467 the state afterwards. */
468 res
= pc
.fs_is_nfs () ? fstat_by_nfs_ea (buf
) : fstat_by_handle (buf
);
469 bool no_handle
= nohandle ();
472 nohandle (no_handle
);
473 set_io_handle (NULL
);
476 res
= fstat_by_name (buf
);
482 fhandler_base::fstat_helper (struct __stat64
*buf
,
483 DWORD nNumberOfLinks
)
486 FILE_COMPRESSION_INFORMATION fci
;
487 HANDLE h
= get_stat_handle ();
488 PFILE_NETWORK_OPEN_INFORMATION pfnoi
= pc
.fnoi ();
489 ULONG attributes
= pc
.file_attributes ();
491 to_timestruc_t ((PFILETIME
) &pfnoi
->LastAccessTime
, &buf
->st_atim
);
492 to_timestruc_t ((PFILETIME
) &pfnoi
->LastWriteTime
, &buf
->st_mtim
);
493 /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
494 (FAT for instance). If so, it's faked using LastWriteTime. */
495 to_timestruc_t (pfnoi
->ChangeTime
.QuadPart
? (PFILETIME
) &pfnoi
->ChangeTime
496 : (PFILETIME
) &pfnoi
->LastWriteTime
,
498 to_timestruc_t ((PFILETIME
) &pfnoi
->CreationTime
, &buf
->st_birthtim
);
499 buf
->st_rdev
= buf
->st_dev
= get_dev ();
500 buf
->st_size
= (_off64_t
) pfnoi
->EndOfFile
.QuadPart
;
501 /* The number of links to a directory includes the number of subdirectories
502 in the directory, since all those subdirectories point to it. However,
503 this is painfully slow, so we do without it. */
505 buf
->st_nlink
= pc
.ndisk_links (nNumberOfLinks
);
507 buf
->st_nlink
= nNumberOfLinks
;
510 /* Enforce namehash as inode number on untrusted file systems. */
511 if (ino
&& pc
.isgood_inode (ino
))
512 buf
->st_ino
= (__ino64_t
) ino
;
514 buf
->st_ino
= get_ino ();
516 buf
->st_blksize
= PREFERRED_IO_BLKSIZE
;
518 if (pfnoi
->AllocationSize
.QuadPart
>= 0LL)
519 /* A successful NtQueryInformationFile returns the allocation size
520 correctly for compressed and sparse files as well. */
521 buf
->st_blocks
= (pfnoi
->AllocationSize
.QuadPart
+ S_BLKSIZE
- 1)
523 else if (::has_attribute (attributes
, FILE_ATTRIBUTE_COMPRESSED
524 | FILE_ATTRIBUTE_SPARSE_FILE
)
525 && h
&& !is_fs_special ()
526 && !NtQueryInformationFile (h
, &st
, (PVOID
) &fci
, sizeof fci
,
527 FileCompressionInformation
))
528 /* Otherwise we request the actual amount of bytes allocated for
529 compressed and sparsed files. */
530 buf
->st_blocks
= (fci
.CompressedFileSize
.QuadPart
+ S_BLKSIZE
- 1)
533 /* Otherwise compute no. of blocks from file size. */
534 buf
->st_blocks
= (buf
->st_size
+ S_BLKSIZE
- 1) / S_BLKSIZE
;
537 /* Using a side effect: get_file_attributes checks for directory.
538 This is used, to set S_ISVTX, if needed. */
540 buf
->st_mode
= S_IFDIR
;
541 else if (pc
.issymlink ())
543 buf
->st_size
= pc
.get_symlink_length ();
544 /* symlinks are everything for everyone! */
545 buf
->st_mode
= S_IFLNK
| S_IRWXU
| S_IRWXG
| S_IRWXO
;
546 get_file_attribute (h
, pc
, NULL
,
547 &buf
->st_uid
, &buf
->st_gid
);
550 else if (pc
.issocket ())
551 buf
->st_mode
= S_IFSOCK
;
553 if (!get_file_attribute (is_fs_special () && !pc
.issocket () ? NULL
: h
, pc
,
554 &buf
->st_mode
, &buf
->st_uid
, &buf
->st_gid
))
556 /* If read-only attribute is set, modify ntsec return value */
557 if (::has_attribute (attributes
, FILE_ATTRIBUTE_READONLY
)
558 && !pc
.isdir () && !pc
.issymlink ())
559 buf
->st_mode
&= ~(S_IWUSR
| S_IWGRP
| S_IWOTH
);
561 if (buf
->st_mode
& S_IFMT
)
563 else if (!is_fs_special ())
564 buf
->st_mode
|= S_IFREG
;
567 buf
->st_dev
= buf
->st_rdev
= dev ();
568 buf
->st_mode
= dev ().mode
;
574 buf
->st_mode
|= STD_RBITS
;
576 if (!::has_attribute (attributes
, FILE_ATTRIBUTE_READONLY
))
577 buf
->st_mode
|= STD_WBITS
;
578 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
581 buf
->st_mode
|= S_IFDIR
| STD_WBITS
| STD_XBITS
;
582 else if (buf
->st_mode
& S_IFMT
)
584 else if (is_fs_special ())
586 buf
->st_dev
= buf
->st_rdev
= dev ();
587 buf
->st_mode
= dev ().mode
;
592 buf
->st_mode
|= S_IFREG
;
593 /* Check suffix for executable file. */
594 if (pc
.exec_state () != is_executable
)
596 PUNICODE_STRING path
= pc
.get_nt_native_path ();
598 if (RtlEqualUnicodePathSuffix (path
, &ro_u_exe
, TRUE
)
599 || RtlEqualUnicodePathSuffix (path
, &ro_u_lnk
, TRUE
)
600 || RtlEqualUnicodePathSuffix (path
, &ro_u_com
, TRUE
))
603 /* No known suffix, check file header. This catches binaries and
605 if (pc
.exec_state () == dont_know_if_executable
)
607 OBJECT_ATTRIBUTES attr
;
611 /* We have to re-open the file. Either the file is not opened
612 for reading, or the read will change the file position of the
614 pc
.init_reopen_attr (&attr
, h
);
615 status
= NtOpenFile (&h
, SYNCHRONIZE
| FILE_READ_DATA
,
616 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
617 FILE_OPEN_FOR_BACKUP_INTENT
618 | FILE_SYNCHRONOUS_IO_NONALERT
);
619 if (!NT_SUCCESS (status
))
620 debug_printf ("%p = NtOpenFile(%S)", status
,
621 pc
.get_nt_native_path ());
624 LARGE_INTEGER off
= { QuadPart
:0LL };
627 status
= NtReadFile (h
, NULL
, NULL
, NULL
,
628 &io
, magic
, 3, &off
, NULL
);
629 if (!NT_SUCCESS (status
))
630 debug_printf ("%p = NtReadFile(%S)", status
,
631 pc
.get_nt_native_path ());
632 else if (has_exec_chars (magic
, io
.Information
))
634 /* Heureka, it's an executable */
636 buf
->st_mode
|= STD_XBITS
;
642 if (pc
.exec_state () == is_executable
)
643 buf
->st_mode
|= STD_XBITS
;
645 /* This fakes the permissions of all files to match the current umask. */
646 buf
->st_mode
&= ~(cygheap
->umask
);
647 /* If the FS supports ACLs, we're here because we couldn't even open
648 the file for READ_CONTROL access. Chances are high that the file's
649 security descriptor has no ACE for "Everyone", so we should not fake
650 any access for "others". */
652 buf
->st_mode
&= ~(S_IROTH
| S_IWOTH
| S_IXOTH
);
656 syscall_printf ("0 = fstat (%S, %p) st_atime=%x st_size=%D, st_mode=%p, "
657 "st_ino=%D, sizeof=%d",
658 pc
.get_nt_native_path (), buf
, buf
->st_atime
, buf
->st_size
,
659 buf
->st_mode
, buf
->st_ino
, sizeof (*buf
));
664 fhandler_disk_file::fstat (struct __stat64
*buf
)
666 return fstat_fs (buf
);
670 fhandler_disk_file::fstatvfs (struct statvfs
*sfs
)
672 int ret
= -1, opened
= 0;
675 FILE_FS_FULL_SIZE_INFORMATION full_fsi
;
676 FILE_FS_SIZE_INFORMATION fsi
;
677 /* We must not use the stat handle here, even if it exists. The handle
678 has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
679 mount point, it points to the FS of the mount point, rather than to the
681 HANDLE fh
= get_handle ();
685 OBJECT_ATTRIBUTES attr
;
686 opened
= NT_SUCCESS (NtOpenFile (&fh
, READ_CONTROL
,
687 pc
.get_object_attr (attr
, sec_none_nih
),
688 &io
, FILE_SHARE_VALID_FLAGS
,
689 FILE_OPEN_FOR_BACKUP_INTENT
));
692 /* Can't open file. Try again with parent dir. */
693 UNICODE_STRING dirname
;
694 RtlSplitUnicodePath (pc
.get_nt_native_path (), &dirname
, NULL
);
695 attr
.ObjectName
= &dirname
;
696 opened
= NT_SUCCESS (NtOpenFile (&fh
, READ_CONTROL
, &attr
, &io
,
697 FILE_SHARE_VALID_FLAGS
,
698 FILE_OPEN_FOR_BACKUP_INTENT
));
704 sfs
->f_files
= ULONG_MAX
;
705 sfs
->f_ffree
= ULONG_MAX
;
706 sfs
->f_favail
= ULONG_MAX
;
707 sfs
->f_fsid
= pc
.fs_serial_number ();
708 sfs
->f_flag
= pc
.fs_flags ();
709 sfs
->f_namemax
= pc
.fs_name_len ();
710 /* Get allocation related information. Try to get "full" information
711 first, which is only available since W2K. If that fails, try to
712 retrieve normal allocation information. */
713 status
= NtQueryVolumeInformationFile (fh
, &io
, &full_fsi
, sizeof full_fsi
,
714 FileFsFullSizeInformation
);
715 if (NT_SUCCESS (status
))
717 sfs
->f_bsize
= full_fsi
.BytesPerSector
* full_fsi
.SectorsPerAllocationUnit
;
718 sfs
->f_frsize
= sfs
->f_bsize
;
719 sfs
->f_blocks
= full_fsi
.TotalAllocationUnits
.LowPart
;
720 sfs
->f_bfree
= full_fsi
.ActualAvailableAllocationUnits
.LowPart
;
721 sfs
->f_bavail
= full_fsi
.CallerAvailableAllocationUnits
.LowPart
;
722 if (sfs
->f_bfree
> sfs
->f_bavail
)
724 /* Quotas active. We can't trust TotalAllocationUnits. */
725 NTFS_VOLUME_DATA_BUFFER nvdb
;
727 status
= NtFsControlFile (fh
, NULL
, NULL
, NULL
, &io
,
728 FSCTL_GET_NTFS_VOLUME_DATA
,
729 NULL
, 0, &nvdb
, sizeof nvdb
);
730 if (!NT_SUCCESS (status
))
731 debug_printf ("%p = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
732 status
, pc
.get_nt_native_path ());
734 sfs
->f_blocks
= nvdb
.TotalClusters
.QuadPart
;
740 status
= NtQueryVolumeInformationFile (fh
, &io
, &fsi
, sizeof fsi
,
741 FileFsSizeInformation
);
742 if (!NT_SUCCESS (status
))
744 __seterrno_from_nt_status (status
);
747 sfs
->f_bsize
= fsi
.BytesPerSector
* fsi
.SectorsPerAllocationUnit
;
748 sfs
->f_frsize
= sfs
->f_bsize
;
749 sfs
->f_blocks
= fsi
.TotalAllocationUnits
.LowPart
;
750 sfs
->f_bfree
= fsi
.AvailableAllocationUnits
.LowPart
;
751 sfs
->f_bavail
= sfs
->f_bfree
;
757 syscall_printf ("%d = fstatvfs (%s, %p)", ret
, get_name (), sfs
);
762 fhandler_disk_file::fchmod (mode_t mode
)
764 extern int chmod_device (path_conv
& pc
, mode_t mode
);
770 if (pc
.is_fs_special ())
771 return chmod_device (pc
, mode
);
775 query_open (query_write_dac
);
776 if (!(oret
= open (O_BINARY
, 0)))
778 /* Need WRITE_DAC|WRITE_OWNER to write ACLs. */
781 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
782 query_open (query_write_attributes
);
783 if (!(oret
= open (O_BINARY
, 0)))
790 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
791 Only type and mode have to be set. Apparently type isn't checked
792 for consistency, so it's sufficent to set it to NF3REG all the time. */
794 FILE_FULL_EA_INFORMATION ffei
;
795 char buf
[sizeof (NFS_V3_ATTR
) + sizeof (fattr3
)];
797 ffei_buf
.ffei
.NextEntryOffset
= 0;
798 ffei_buf
.ffei
.Flags
= 0;
799 ffei_buf
.ffei
.EaNameLength
= sizeof (NFS_V3_ATTR
) - 1;
800 ffei_buf
.ffei
.EaValueLength
= sizeof (fattr3
);
801 strcpy (ffei_buf
.ffei
.EaName
, NFS_V3_ATTR
);
802 fattr3
*nfs_attr
= (fattr3
*) (ffei_buf
.ffei
.EaName
803 + ffei_buf
.ffei
.EaNameLength
+ 1);
804 memset (nfs_attr
, 0, sizeof (fattr3
));
805 nfs_attr
->type
= NF3REG
;
806 nfs_attr
->mode
= mode
;
807 status
= NtSetEaFile (get_handle (), &io
,
808 &ffei_buf
.ffei
, sizeof ffei_buf
);
809 if (!NT_SUCCESS (status
))
810 __seterrno_from_nt_status (status
);
820 if (!set_file_attribute (get_handle (), pc
,
821 ILLEGAL_UID
, ILLEGAL_GID
, mode
))
825 /* If the mode has any write bits set, the DOS R/O flag is in the way. */
826 if (mode
& (S_IWUSR
| S_IWGRP
| S_IWOTH
))
827 pc
&= (DWORD
) ~FILE_ATTRIBUTE_READONLY
;
828 else if (!pc
.has_acls ()) /* Never set DOS R/O if security is used. */
829 pc
|= (DWORD
) FILE_ATTRIBUTE_READONLY
;
831 pc
|= (DWORD
) FILE_ATTRIBUTE_SYSTEM
;
833 status
= NtSetAttributesFile (get_handle (), pc
.file_attributes ());
834 /* MVFS needs a good amount of kicking to be convinced that it has to write
835 back metadata changes and to invalidate the cached metadata information
836 stored for the given handle. This method to open a second handle to
837 the file and write the same metadata information twice has been found
838 experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
839 if (pc
.fs_is_mvfs () && NT_SUCCESS (status
) && !oret
)
841 OBJECT_ATTRIBUTES attr
;
844 pc
.init_reopen_attr (&attr
, get_handle ());
845 if (NT_SUCCESS (NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
, &attr
, &io
,
846 FILE_SHARE_VALID_FLAGS
,
847 FILE_OPEN_FOR_BACKUP_INTENT
)))
849 NtSetAttributesFile (fh
, pc
.file_attributes ());
853 /* Correct NTFS security attributes have higher priority */
856 if (!NT_SUCCESS (status
))
857 __seterrno_from_nt_status (status
);
870 fhandler_disk_file::fchown (__uid32_t uid
, __gid32_t gid
)
876 /* fake - if not supported, pretend we're like win95
877 where it just works */
878 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
884 query_open (query_write_control
);
885 if (!(oret
= fhandler_disk_file::open (O_BINARY
, 0)))
893 int res
= get_file_attribute (get_handle (), pc
, &attrib
, &old_uid
, NULL
);
896 /* Typical Windows default ACLs can contain permissions for one
897 group, while being owned by another user/group. The permission
898 bits returned above are pretty much useless then. Creating a
899 new ACL with these useless permissions results in a potentially
900 broken symlink. So what we do here is to set the underlying
901 permissions of symlinks to a sensible value which allows the
902 world to read the symlink and only the new owner to change it. */
904 attrib
= S_IFLNK
| STD_RBITS
| STD_WBITS
;
905 res
= set_file_attribute (get_handle (), pc
, uid
, gid
, attrib
);
906 /* If you're running a Samba server which has no winbidd running, the
907 uid<->SID mapping is disfunctional. Even trying to chown to your
908 own account fails since the account used on the server is the UNIX
909 account which gets used for the standard user mapping. This is a
910 default mechanism which doesn't know your real Windows SID.
911 There are two possible error codes in different Samba releases for
912 this situation, one of them is unfortunately the not very significant
913 STATUS_ACCESS_DENIED. Instead of relying on the error codes, we're
914 using the below very simple heuristic. If set_file_attribute failed,
915 and the original user account was either already unknown, or one of
916 the standard UNIX accounts, we're faking success. */
917 if (res
== -1 && pc
.fs_is_samba ())
921 if (old_uid
== ILLEGAL_UID
922 || (sid
.getfrompw (internal_getpwuid (old_uid
))
923 && EqualPrefixSid (sid
, well_known_samba_unix_user_fake_sid
)))
925 debug_printf ("Faking chown worked on standalone Samba");
937 fhandler_disk_file::facl (int cmd
, int nentries
, __aclent32_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
)
965 aclbufp
[0].a_type
= USER_OBJ
;
966 aclbufp
[0].a_id
= st
.st_uid
;
967 aclbufp
[0].a_perm
= (st
.st_mode
& S_IRWXU
) >> 6;
968 aclbufp
[1].a_type
= GROUP_OBJ
;
969 aclbufp
[1].a_id
= st
.st_gid
;
970 aclbufp
[1].a_perm
= (st
.st_mode
& S_IRWXG
) >> 3;
971 aclbufp
[2].a_type
= OTHER_OBJ
;
972 aclbufp
[2].a_id
= ILLEGAL_GID
;
973 aclbufp
[2].a_perm
= st
.st_mode
& S_IRWXO
;
974 aclbufp
[3].a_type
= CLASS_OBJ
;
975 aclbufp
[3].a_id
= ILLEGAL_GID
;
976 aclbufp
[3].a_perm
= S_IRWXU
| S_IRWXG
| S_IRWXO
;
977 res
= MIN_ACL_ENTRIES
;
982 res
= MIN_ACL_ENTRIES
;
991 if ((cmd
== SETACL
&& !get_handle ())
992 || (cmd
!= SETACL
&& !get_stat_handle ()))
994 query_open (cmd
== SETACL
? query_write_control
: query_read_control
);
995 if (!(oret
= open (O_BINARY
, 0)))
997 if (cmd
== GETACL
|| cmd
== GETACLCNT
)
998 goto cant_access_acl
;
1005 if (!aclsort32 (nentries
, 0, aclbufp
))
1008 res
= setacl (get_handle (), pc
, nentries
, aclbufp
, rw
);
1012 FILE_BASIC_INFORMATION fbi
;
1013 fbi
.CreationTime
.QuadPart
1014 = fbi
.LastAccessTime
.QuadPart
1015 = fbi
.LastWriteTime
.QuadPart
1016 = fbi
.ChangeTime
.QuadPart
= 0LL;
1017 fbi
.FileAttributes
= (pc
.file_attributes ()
1018 & ~FILE_ATTRIBUTE_READONLY
)
1019 ?: FILE_ATTRIBUTE_NORMAL
;
1020 NtSetInformationFile (get_handle (), &io
, &fbi
, sizeof fbi
,
1021 FileBasicInformation
);
1029 res
= getacl (get_stat_handle (), pc
, nentries
, aclbufp
);
1030 /* For this ENOSYS case, see security.cc:get_file_attribute(). */
1031 if (res
== -1 && get_errno () == ENOSYS
)
1032 goto cant_access_acl
;
1035 res
= getacl (get_stat_handle (), pc
, 0, NULL
);
1037 if (res
== -1 && get_errno () == ENOSYS
)
1038 goto cant_access_acl
;
1053 fhandler_disk_file::fgetxattr (const char *name
, void *value
, size_t size
)
1055 if (pc
.is_fs_special ())
1057 set_errno (ENOTSUP
);
1060 return read_ea (get_handle (), pc
, name
, (char *) value
, size
);
1064 fhandler_disk_file::fsetxattr (const char *name
, const void *value
, size_t size
,
1067 if (pc
.is_fs_special ())
1069 set_errno (ENOTSUP
);
1072 return write_ea (get_handle (), pc
, name
, (const char *) value
, size
, flags
);
1076 fhandler_disk_file::fadvise (_off64_t offset
, _off64_t length
, int advice
)
1078 if (advice
< POSIX_FADV_NORMAL
|| advice
> POSIX_FADV_NOREUSE
)
1084 /* Windows only supports advice flags for the whole file. We're using
1085 a simplified test here so that we don't have to ask for the actual
1086 file size. Length == 0 means all bytes starting at offset anyway.
1087 So we only actually follow the advice, if it's given for offset == 0. */
1091 /* We only support normal and sequential mode for now. Everything which
1092 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1093 if (advice
!= POSIX_FADV_SEQUENTIAL
)
1094 advice
= POSIX_FADV_NORMAL
;
1097 FILE_MODE_INFORMATION fmi
;
1098 NTSTATUS status
= NtQueryInformationFile (get_handle (), &io
,
1100 FileModeInformation
);
1101 if (!NT_SUCCESS (status
))
1102 __seterrno_from_nt_status (status
);
1105 fmi
.Mode
&= ~FILE_SEQUENTIAL_ONLY
;
1106 if (advice
== POSIX_FADV_SEQUENTIAL
)
1107 fmi
.Mode
|= FILE_SEQUENTIAL_ONLY
;
1108 status
= NtSetInformationFile (get_handle (), &io
, &fmi
, sizeof fmi
,
1109 FileModeInformation
);
1110 if (NT_SUCCESS (status
))
1112 __seterrno_from_nt_status (status
);
1119 fhandler_disk_file::ftruncate (_off64_t length
, bool allow_truncate
)
1123 if (length
< 0 || !get_handle ())
1125 else if (pc
.isdir ())
1127 else if (!(get_access () & GENERIC_WRITE
))
1133 FILE_STANDARD_INFORMATION fsi
;
1134 FILE_END_OF_FILE_INFORMATION feofi
;
1136 status
= NtQueryInformationFile (get_handle (), &io
, &fsi
, sizeof fsi
,
1137 FileStandardInformation
);
1138 if (!NT_SUCCESS (status
))
1140 __seterrno_from_nt_status (status
);
1144 /* If called through posix_fallocate, silently succeed if length
1145 is less than the file's actual length. */
1146 if (!allow_truncate
&& length
< fsi
.EndOfFile
.QuadPart
)
1149 feofi
.EndOfFile
.QuadPart
= length
;
1150 /* Create sparse files only when called through ftruncate, not when
1151 called through posix_fallocate. */
1153 && (pc
.fs_flags () & FILE_SUPPORTS_SPARSE_FILES
)
1154 && length
>= fsi
.EndOfFile
.QuadPart
+ (128 * 1024))
1156 status
= NtFsControlFile (get_handle (), NULL
, NULL
, NULL
, &io
,
1157 FSCTL_SET_SPARSE
, NULL
, 0, NULL
, 0);
1158 syscall_printf ("%p = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1159 status
, pc
.get_nt_native_path ());
1161 status
= NtSetInformationFile (get_handle (), &io
,
1162 &feofi
, sizeof feofi
,
1163 FileEndOfFileInformation
);
1164 if (!NT_SUCCESS (status
))
1165 __seterrno_from_nt_status (status
);
1173 fhandler_disk_file::link (const char *newpath
)
1175 size_t nlen
= strlen (newpath
);
1176 path_conv
newpc (newpath
, PC_SYM_NOFOLLOW
| PC_POSIX
| PC_NULLEMPTY
, stat_suffixes
);
1179 set_errno (newpc
.error
);
1183 if (newpc
.exists ())
1185 syscall_printf ("file '%S' exists?", newpc
.get_nt_native_path ());
1190 if (isdirsep (newpath
[nlen
- 1]) || has_dot_last_component (newpath
, false))
1196 char new_buf
[nlen
+ 5];
1199 /* If the original file is a lnk special file (except for sockets),
1200 and if the original file has a .lnk suffix, add one to the hardlink
1202 if (pc
.is_lnk_special () && !pc
.issocket ()
1203 && RtlEqualUnicodePathSuffix (pc
.get_nt_native_path (),
1206 /* Shortcut hack. */
1207 stpcpy (stpcpy (new_buf
, newpath
), ".lnk");
1209 newpc
.check (newpath
, PC_SYM_NOFOLLOW
);
1211 else if (!pc
.isdir ()
1213 && RtlEqualUnicodePathSuffix (pc
.get_nt_native_path (),
1215 && !RtlEqualUnicodePathSuffix (newpc
.get_nt_native_path (),
1218 /* Executable hack. */
1219 stpcpy (stpcpy (new_buf
, newpath
), ".exe");
1221 newpc
.check (newpath
, PC_SYM_NOFOLLOW
);
1225 /* We only need READ_CONTROL access so the handle returned in pc is
1226 sufficient. And if the file couldn't be opened with READ_CONTROL
1227 access in path_conv, we won't be able to do it here anyway. */
1228 HANDLE fh
= get_stat_handle ();
1234 PUNICODE_STRING tgt
= newpc
.get_nt_native_path ();
1235 ULONG size
= sizeof (FILE_LINK_INFORMATION
) + tgt
->Length
;
1236 PFILE_LINK_INFORMATION pfli
= (PFILE_LINK_INFORMATION
) alloca (size
);
1237 pfli
->ReplaceIfExists
= FALSE
;
1238 pfli
->RootDirectory
= NULL
;
1239 memcpy (pfli
->FileName
, tgt
->Buffer
, pfli
->FileNameLength
= tgt
->Length
);
1243 status
= NtSetInformationFile (fh
, &io
, pfli
, size
, FileLinkInformation
);
1244 if (!NT_SUCCESS (status
))
1246 if (status
== STATUS_INVALID_DEVICE_REQUEST
)
1248 /* FS doesn't support hard links. Linux returns EPERM. */
1254 __seterrno_from_nt_status (status
);
1262 fhandler_disk_file::utimens (const struct timespec
*tvp
)
1264 return utimens_fs (tvp
);
1268 fhandler_base::utimens_fs (const struct timespec
*tvp
)
1270 struct timespec timeofday
;
1271 struct timespec tmp
[2];
1272 bool closeit
= false;
1276 query_open (query_write_attributes
);
1277 if (!open_fs (O_BINARY
, 0))
1279 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1280 to change the timestamps. Unfortunately it's not sufficient for a
1281 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1282 for writing, though this fails for R/O files of course. */
1283 query_open (no_query
);
1284 if (!open_fs (O_WRONLY
| O_BINARY
, 0))
1286 syscall_printf ("Opening file failed");
1293 clock_gettime (CLOCK_REALTIME
, &timeofday
);
1295 tmp
[1] = tmp
[0] = timeofday
;
1298 if ((tvp
[0].tv_nsec
< UTIME_NOW
|| tvp
[0].tv_nsec
> 999999999L)
1299 || (tvp
[1].tv_nsec
< UTIME_NOW
|| tvp
[1].tv_nsec
> 999999999L))
1306 tmp
[0] = (tvp
[0].tv_nsec
== UTIME_NOW
) ? timeofday
: tvp
[0];
1307 tmp
[1] = (tvp
[1].tv_nsec
== UTIME_NOW
) ? timeofday
: tvp
[1];
1309 debug_printf ("incoming lastaccess %08x %08x", tmp
[0].tv_sec
, tmp
[0].tv_nsec
);
1312 FILE_BASIC_INFORMATION fbi
;
1314 fbi
.CreationTime
.QuadPart
= 0LL;
1315 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1316 timespec_to_filetime (&tmp
[0], (LPFILETIME
) &fbi
.LastAccessTime
);
1317 timespec_to_filetime (&tmp
[1], (LPFILETIME
) &fbi
.LastWriteTime
);
1318 fbi
.ChangeTime
.QuadPart
= 0LL;
1319 fbi
.FileAttributes
= 0;
1320 NTSTATUS status
= NtSetInformationFile (get_handle (), &io
, &fbi
, sizeof fbi
,
1321 FileBasicInformation
);
1322 /* For this special case for MVFS see the comment in
1323 fhandler_disk_file::fchmod. */
1324 if (pc
.fs_is_mvfs () && NT_SUCCESS (status
) && !closeit
)
1326 OBJECT_ATTRIBUTES attr
;
1329 pc
.init_reopen_attr (&attr
, get_handle ());
1330 if (NT_SUCCESS (NtOpenFile (&fh
, FILE_WRITE_ATTRIBUTES
, &attr
, &io
,
1331 FILE_SHARE_VALID_FLAGS
,
1332 FILE_OPEN_FOR_BACKUP_INTENT
)))
1334 NtSetInformationFile (fh
, &io
, &fbi
, sizeof fbi
,
1335 FileBasicInformation
);
1341 /* Opening a directory on a 9x share from a NT machine works(!), but
1342 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1343 if (!NT_SUCCESS (status
) && status
!= STATUS_NOT_SUPPORTED
)
1345 __seterrno_from_nt_status (status
);
1351 fhandler_disk_file::fhandler_disk_file () :
1356 fhandler_disk_file::fhandler_disk_file (path_conv
&pc
) :
1363 fhandler_disk_file::open (int flags
, mode_t mode
)
1365 return open_fs (flags
, mode
);
1369 fhandler_base::open_fs (int flags
, mode_t mode
)
1371 /* Unfortunately NT allows to open directories for writing, but that's
1372 disallowed according to SUSv3. */
1373 if (pc
.isdir () && (flags
& O_ACCMODE
) != O_RDONLY
)
1379 int res
= fhandler_base::open (flags
| O_DIROPEN
, mode
);
1383 /* This is for file systems known for having a buggy CreateFile call
1384 which might return a valid HANDLE without having actually opened
1386 The only known file system to date is the SUN NFS Solstice Client 3.1
1387 which returns a valid handle when trying to open a file in a nonexistent
1389 if (pc
.has_buggy_open () && !pc
.exists ())
1391 debug_printf ("Buggy open detected.");
1397 ino
= pc
.get_ino_by_handle (get_handle ());
1398 /* A unique ID is necessary to recognize fhandler entries which are
1399 duplicated by dup(2) or fork(2). */
1400 AllocateLocallyUniqueId ((PLUID
) &unique_id
);
1403 syscall_printf ("%d = fhandler_disk_file::open (%S, %p)", res
,
1404 pc
.get_nt_native_path (), flags
);
1409 fhandler_disk_file::pread (void *buf
, size_t count
, _off64_t offset
)
1412 _off64_t curpos
= lseek (0, SEEK_CUR
);
1413 if (curpos
< 0 || lseek (offset
, SEEK_SET
) < 0)
1417 size_t tmp_count
= count
;
1418 read (buf
, tmp_count
);
1419 if (lseek (curpos
, SEEK_SET
) >= 0)
1420 res
= (ssize_t
) tmp_count
;
1424 debug_printf ("%d = pread (%p, %d, %d)\n", res
, buf
, count
, offset
);
1429 fhandler_disk_file::pwrite (void *buf
, size_t count
, _off64_t offset
)
1432 _off64_t curpos
= lseek (0, SEEK_CUR
);
1433 if (curpos
< 0 || lseek (offset
, SEEK_SET
) < 0)
1437 res
= (ssize_t
) write (buf
, count
);
1438 if (lseek (curpos
, SEEK_SET
) < 0)
1441 debug_printf ("%d = pwrite (%p, %d, %d)\n", res
, buf
, count
, offset
);
1446 fhandler_disk_file::mkdir (mode_t mode
)
1449 SECURITY_ATTRIBUTES sa
= sec_none_nih
;
1452 OBJECT_ATTRIBUTES attr
;
1454 PFILE_FULL_EA_INFORMATION p
= NULL
;
1457 if (pc
.fs_is_nfs ())
1459 /* When creating a dir on an NFS share, we have to set the
1460 file mode by writing a NFS fattr3 structure with the
1461 correct mode bits set. */
1462 plen
= sizeof (FILE_FULL_EA_INFORMATION
) + sizeof (NFS_V3_ATTR
)
1464 p
= (PFILE_FULL_EA_INFORMATION
) alloca (plen
);
1465 p
->NextEntryOffset
= 0;
1467 p
->EaNameLength
= sizeof (NFS_V3_ATTR
) - 1;
1468 p
->EaValueLength
= sizeof (fattr3
);
1469 strcpy (p
->EaName
, NFS_V3_ATTR
);
1470 fattr3
*nfs_attr
= (fattr3
*) (p
->EaName
+ p
->EaNameLength
+ 1);
1471 memset (nfs_attr
, 0, sizeof (fattr3
));
1472 nfs_attr
->type
= NF3DIR
;
1473 nfs_attr
->mode
= (mode
& 07777) & ~cygheap
->umask
;
1475 status
= NtCreateFile (&dir
, FILE_LIST_DIRECTORY
| SYNCHRONIZE
,
1476 pc
.get_object_attr (attr
, sa
), &io
, NULL
,
1477 FILE_ATTRIBUTE_DIRECTORY
, FILE_SHARE_VALID_FLAGS
,
1479 FILE_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
1480 | FILE_OPEN_FOR_BACKUP_INTENT
,
1482 if (NT_SUCCESS (status
))
1485 set_file_attribute (dir
, pc
, ILLEGAL_UID
, ILLEGAL_GID
,
1486 S_JUSTCREATED
| S_IFDIR
1487 | ((mode
& 07777) & ~cygheap
->umask
));
1492 __seterrno_from_nt_status (status
);
1498 fhandler_disk_file::rmdir ()
1500 extern NTSTATUS
unlink_nt (path_conv
&pc
);
1504 set_errno (ENOTDIR
);
1513 NTSTATUS status
= unlink_nt (pc
);
1515 /* Check for existence of remote dirs after trying to delete them.
1517 - Sometimes SMB indicates failure when it really succeeds.
1518 - Removing a directory on a Samba drive using an old Samba version
1519 sometimes doesn't return an error, if the directory can't be removed
1520 because it's not empty. */
1523 OBJECT_ATTRIBUTES attr
;
1524 FILE_BASIC_INFORMATION fbi
;
1527 q_status
= NtQueryAttributesFile (pc
.get_object_attr (attr
, sec_none_nih
), &fbi
);
1528 if (!NT_SUCCESS (status
) && q_status
== STATUS_OBJECT_NAME_NOT_FOUND
)
1529 status
= STATUS_SUCCESS
;
1530 else if (NT_SUCCESS (status
) && NT_SUCCESS (q_status
))
1531 status
= STATUS_DIRECTORY_NOT_EMPTY
;
1533 if (!NT_SUCCESS (status
))
1535 __seterrno_from_nt_status (status
);
1541 /* This is the minimal number of entries which fit into the readdir cache.
1542 The number of bytes allocated by the cache is determined by this number,
1543 To tune caching, just tweak this number. To get a feeling for the size,
1544 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1546 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1548 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1549 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1550 + (NAME_MAX + 1) * sizeof (WCHAR)))
1554 char __cache
[DIR_BUF_SIZE
]; /* W2K needs this buffer 8 byte aligned. */
1558 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1559 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1561 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1564 fhandler_disk_file::opendir (int fd
)
1570 set_errno (ENOTDIR
);
1571 else if ((dir
= (DIR *) malloc (sizeof (DIR))) == NULL
)
1573 else if ((dir
->__d_dirname
= (char *) malloc ( sizeof (struct __DIR_cache
)))
1579 else if ((dir
->__d_dirent
=
1580 (struct dirent
*) malloc (sizeof (struct dirent
))) == NULL
)
1588 if (cfd
< 0 && fd
< 0)
1591 dir
->__d_dirent
->__d_version
= __DIRENT_VERSION
;
1592 dir
->__d_cookie
= __DIRENT_COOKIE
;
1593 dir
->__handle
= INVALID_HANDLE_VALUE
;
1594 dir
->__d_position
= 0;
1595 dir
->__flags
= (get_name ()[0] == '/' && get_name ()[1] == '\0')
1596 ? dirent_isroot
: 0;
1597 dir
->__d_internal
= (unsigned) new __DIR_mounts (get_name ());
1598 d_cachepos (dir
) = 0;
1600 if (!pc
.iscygdrive ())
1604 /* opendir() case. Initialize with given directory name and
1605 NULL directory handle. */
1606 OBJECT_ATTRIBUTES attr
;
1609 /* Tools like ls(1) call dirfd() to fetch the directory
1610 descriptor for calls to facl or fstat. The tight access mask
1611 used so far is not sufficient to reuse the handle for these
1612 calls, instead the facl/fstat calls find the handle to be
1613 unusable and have to re-open the file for reading attributes
1614 and control data. So, what we do here is to try to open the
1615 directory with more relaxed access mask which enables to use
1616 the handle for the aforementioned purpose. This should work
1617 in almost all cases. Only if it doesn't work due to
1618 permission problems, we drop the additional access bits and
1620 ACCESS_MASK fstat_mask
= READ_CONTROL
| FILE_READ_ATTRIBUTES
;
1624 status
= NtOpenFile (&get_handle (),
1625 SYNCHRONIZE
| FILE_LIST_DIRECTORY
1627 pc
.get_object_attr (attr
, sec_none_nih
),
1628 &io
, FILE_SHARE_VALID_FLAGS
,
1629 FILE_SYNCHRONOUS_IO_NONALERT
1630 | FILE_OPEN_FOR_BACKUP_INTENT
1631 | FILE_DIRECTORY_FILE
);
1632 if (!NT_SUCCESS (status
))
1634 if (status
== STATUS_ACCESS_DENIED
&& fstat_mask
)
1638 __seterrno_from_nt_status (status
);
1643 while (!NT_SUCCESS (status
));
1646 /* FileIdBothDirectoryInformation is apparently unsupported on
1647 XP when accessing directories on UDF. When trying to use it
1648 so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1649 It's not clear if the call isn't also unsupported on other
1650 OS/FS combinations (say, Win2K/CDFS or so). Instead of
1651 testing in readdir for yet another error code, let's use
1652 FileIdBothDirectoryInformation only on filesystems supporting
1653 persistent ACLs, FileBothDirectoryInformation otherwise.
1655 NFS clients hide dangling symlinks from directory queries,
1656 unless you use the FileNamesInformation info class.
1657 On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
1658 works fine, but only if the NFS share is mounted to a drive
1659 letter. TODO: We don't test that here for now, but it might
1660 be worth to test if there's a speed gain in using
1661 FileIdBothDirectoryInformation, because it doesn't require to
1662 open the file to read the inode number. */
1663 if (pc
.hasgood_inode ())
1665 dir
->__flags
|= dirent_set_d_ino
;
1666 if (pc
.fs_is_nfs ())
1667 dir
->__flags
|= dirent_nfs_d_ino
;
1668 else if (wincap
.has_fileid_dirinfo ()
1669 && !pc
.has_buggy_fileid_dirinfo ())
1670 dir
->__flags
|= dirent_get_d_ino
;
1677 /* Filling cfd with `this' (aka storing this in the file
1678 descriptor table should only happen after it's clear that
1679 opendir doesn't fail, otherwise we end up cfree'ing the
1680 fhandler twice, once in opendir() in dir.cc, the second
1681 time on exit. Nasty, nasty... */
1684 if (pc
.iscygdrive ())
1685 cfd
->nohandle (true);
1687 set_close_on_exec (true);
1692 syscall_printf ("%p = opendir (%s)", res
, get_name ());
1696 delete d_mounts (dir
);
1698 free (dir
->__d_dirent
);
1700 free (dir
->__d_dirname
);
1707 readdir_get_ino (const char *path
, bool dot_dot
)
1712 OBJECT_ATTRIBUTES attr
;
1718 fname
= (char *) alloca (strlen (path
) + 4);
1719 char *c
= stpcpy (fname
, path
);
1725 path_conv
pc (path
, PC_SYM_NOFOLLOW
| PC_POSIX
| PC_NOWARN
| PC_KEEP_HANDLE
);
1726 if (pc
.isspecial ())
1728 if (!stat_worker (pc
, &st
))
1731 else if (!pc
.hasgood_inode ())
1732 ino
= hash_path_name (0, pc
.get_nt_native_path ());
1733 else if ((hdl
= pc
.handle ()) != NULL
1734 || NT_SUCCESS (NtOpenFile (&hdl
, READ_CONTROL
,
1735 pc
.get_object_attr (attr
, sec_none_nih
),
1736 &io
, FILE_SHARE_VALID_FLAGS
,
1737 FILE_OPEN_FOR_BACKUP_INTENT
1738 | (pc
.is_rep_symlink ()
1739 ? FILE_OPEN_REPARSE_POINT
: 0)))
1742 ino
= pc
.get_ino_by_handle (hdl
);
1744 ino
= hash_path_name (0, pc
.get_nt_native_path ());
1750 fhandler_disk_file::readdir_helper (DIR *dir
, dirent
*de
, DWORD w32_err
,
1751 DWORD attr
, PUNICODE_STRING fname
)
1756 if ((de
->d_ino
= d_mounts (dir
)->check_missing_mount (fname
)))
1761 return geterrno_from_win_error (w32_err
);
1765 dir
->__flags
&= ~dirent_set_d_ino
;
1768 /* Set d_type if type can be determined from file attributes. For .lnk
1769 symlinks, d_type will be reset below. Reparse points can be NTFS
1770 symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
1772 !(attr
& (~FILE_ATTRIBUTE_VALID_FLAGS
| FILE_ATTRIBUTE_REPARSE_POINT
)))
1774 if (attr
& FILE_ATTRIBUTE_DIRECTORY
)
1775 de
->d_type
= DT_DIR
;
1776 /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
1777 else if (!(attr
& FILE_ATTRIBUTE_SYSTEM
))
1778 de
->d_type
= DT_REG
;
1781 /* Check for directory reparse point. These are potential volume mount
1782 points which have another inode than the underlying directory. */
1783 if ((attr
& (FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_REPARSE_POINT
))
1784 == (FILE_ATTRIBUTE_DIRECTORY
| FILE_ATTRIBUTE_REPARSE_POINT
))
1787 OBJECT_ATTRIBUTES attr
;
1790 InitializeObjectAttributes (&attr
, fname
, pc
.objcaseinsensitive (),
1791 get_handle (), NULL
);
1792 de
->d_type
= readdir_check_reparse_point (&attr
);
1793 if (de
->d_type
== DT_DIR
)
1795 /* Volume mountpoints are treated as directories. We have to fix
1796 the inode number, otherwise we have the inode number of the
1797 mount point, rather than the inode number of the toplevel
1798 directory of the mounted drive. */
1799 if (NT_SUCCESS (NtOpenFile (&reph
, READ_CONTROL
, &attr
, &io
,
1800 FILE_SHARE_VALID_FLAGS
,
1801 FILE_OPEN_FOR_BACKUP_INTENT
)))
1803 de
->d_ino
= pc
.get_ino_by_handle (reph
);
1809 /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
1810 .lnk suffix and set d_type accordingly. */
1811 if ((attr
& (FILE_ATTRIBUTE_DIRECTORY
1812 | FILE_ATTRIBUTE_REPARSE_POINT
1813 | FILE_ATTRIBUTE_READONLY
)) == FILE_ATTRIBUTE_READONLY
1814 && fname
->Length
> 4 * sizeof (WCHAR
))
1816 UNICODE_STRING uname
;
1818 RtlInitCountedUnicodeString (&uname
,
1820 + fname
->Length
/ sizeof (WCHAR
) - 4,
1821 4 * sizeof (WCHAR
));
1822 if (RtlEqualUnicodeString (&uname
, &ro_u_lnk
, TRUE
))
1825 UNICODE_STRING fbuf
;
1828 RtlCopyUnicodeString (&fbuf
, pc
.get_nt_native_path ());
1829 RtlAppendUnicodeToString (&fbuf
, L
"\\");
1830 RtlAppendUnicodeStringToString (&fbuf
, fname
);
1831 fbuf
.Buffer
+= 4; /* Skip leading \??\ */
1832 fbuf
.Length
-= 4 * sizeof (WCHAR
);
1833 if (fbuf
.Buffer
[1] != L
':') /* UNC path */
1835 *(fbuf
.Buffer
+= 2) = L
'\\';
1836 fbuf
.Length
-= 2 * sizeof (WCHAR
);
1838 path_conv
fpath (&fbuf
, PC_SYM_NOFOLLOW
);
1839 if (fpath
.issymlink ())
1841 fname
->Length
-= 4 * sizeof (WCHAR
);
1842 de
->d_type
= DT_LNK
;
1844 else if (fpath
.isfifo ())
1846 fname
->Length
-= 4 * sizeof (WCHAR
);
1847 de
->d_type
= DT_FIFO
;
1849 else if (fpath
.is_fs_special ())
1851 fname
->Length
-= 4 * sizeof (WCHAR
);
1852 de
->d_type
= S_ISCHR (fpath
.dev
.mode
) ? DT_CHR
: DT_BLK
;
1857 sys_wcstombs (de
->d_name
, NAME_MAX
+ 1, fname
->Buffer
,
1858 fname
->Length
/ sizeof (WCHAR
));
1860 /* Don't try to optimize relative to dir->__d_position. On several
1861 filesystems it's no safe bet that "." and ".." entries always
1863 if (de
->d_name
[0] == '.')
1865 if (de
->d_name
[1] == '\0')
1866 dir
->__flags
|= dirent_saw_dot
;
1867 else if (de
->d_name
[1] == '.' && de
->d_name
[2] == '\0')
1868 dir
->__flags
|= dirent_saw_dot_dot
;
1874 fhandler_disk_file::readdir (DIR *dir
, dirent
*de
)
1877 NTSTATUS status
= STATUS_SUCCESS
;
1878 PFILE_ID_BOTH_DIR_INFORMATION buf
= NULL
;
1880 ULONG FileNameLength
;
1881 ULONG FileAttributes
= 0;
1883 UNICODE_STRING fname
;
1885 /* d_cachepos always refers to the next cache entry to use. If it's 0
1886 we must reload the cache. */
1887 if (d_cachepos (dir
) == 0)
1889 if ((dir
->__flags
& dirent_get_d_ino
))
1891 status
= NtQueryDirectoryFile (get_handle (), NULL
, NULL
, NULL
, &io
,
1892 d_cache (dir
), DIR_BUF_SIZE
,
1893 FileIdBothDirectoryInformation
,
1894 FALSE
, NULL
, dir
->__d_position
== 0);
1895 /* FileIdBothDirectoryInformation isn't supported for remote drives
1896 on NT4 and 2K systems, and it's also not supported on 2K at all,
1897 when accessing network drives on any remote OS. There are also
1898 hacked versions of Samba 3.0.x out there (Debian-based it seems),
1899 which return STATUS_NOT_SUPPORTED rather than handling this info
1900 class. We just fall back to using a standard directory query in
1901 this case and note this case using the dirent_get_d_ino flag. */
1902 if (!NT_SUCCESS (status
) && status
!= STATUS_NO_MORE_FILES
1903 && (status
== STATUS_INVALID_LEVEL
1904 || status
== STATUS_NOT_SUPPORTED
1905 || status
== STATUS_INVALID_PARAMETER
1906 || status
== STATUS_INVALID_NETWORK_RESPONSE
1907 || status
== STATUS_INVALID_INFO_CLASS
))
1908 dir
->__flags
&= ~dirent_get_d_ino
;
1909 /* Something weird happens on Samba up to version 3.0.21c, which is
1910 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
1911 nicely, but only up to the 128th entry in the directory. After
1912 reaching this entry, the next call to NtQueryDirectoryFile
1913 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
1914 Why should we care, we can just switch to
1915 FileBothDirectoryInformation, isn't it? Nope! The next call to
1916 NtQueryDirectoryFile(FileBothDirectoryInformation) actually
1917 returns STATUS_NO_MORE_FILES, regardless how many files are left
1918 unread in the directory. This does not happen when using
1919 FileBothDirectoryInformation right from the start, but since
1920 we can't decide whether the server we're talking with has this
1921 bug or not, we end up serving Samba shares always in the slow
1922 mode using FileBothDirectoryInformation. So, what we do here is
1923 to implement the solution suggested by Andrew Tridgell, we just
1924 reread all entries up to dir->d_position using
1925 FileBothDirectoryInformation.
1926 However, We do *not* mark this server as broken and fall back to
1927 using FileBothDirectoryInformation further on. This would slow
1928 down every access to such a server, even for directories under
1929 128 entries. Also, bigger dirs only suffer from one additional
1930 call per full directory scan, which shouldn't be too big a hit.
1931 This can easily be changed if necessary. */
1932 if (status
== STATUS_INVALID_LEVEL
&& dir
->__d_position
)
1934 d_cachepos (dir
) = 0;
1935 for (int cnt
= 0; cnt
< dir
->__d_position
; ++cnt
)
1937 if (d_cachepos (dir
) == 0)
1939 status
= NtQueryDirectoryFile (get_handle (), NULL
, NULL
,
1940 NULL
, &io
, d_cache (dir
),
1942 FileBothDirectoryInformation
,
1943 FALSE
, NULL
, cnt
== 0);
1944 if (!NT_SUCCESS (status
))
1947 buf
= (PFILE_ID_BOTH_DIR_INFORMATION
) (d_cache (dir
)
1948 + d_cachepos (dir
));
1949 if (buf
->NextEntryOffset
== 0)
1950 d_cachepos (dir
) = 0;
1952 d_cachepos (dir
) += buf
->NextEntryOffset
;
1957 if (!(dir
->__flags
& dirent_get_d_ino
))
1958 status
= NtQueryDirectoryFile (get_handle (), NULL
, NULL
, NULL
, &io
,
1959 d_cache (dir
), DIR_BUF_SIZE
,
1960 (dir
->__flags
& dirent_nfs_d_ino
)
1961 ? FileNamesInformation
1962 : FileBothDirectoryInformation
,
1963 FALSE
, NULL
, dir
->__d_position
== 0);
1968 if (status
== STATUS_NO_MORE_FILES
)
1970 else if (!NT_SUCCESS (status
))
1971 debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
1972 status
, RtlNtStatusToDosError (status
));
1975 buf
= (PFILE_ID_BOTH_DIR_INFORMATION
) (d_cache (dir
) + d_cachepos (dir
));
1976 if (buf
->NextEntryOffset
== 0)
1977 d_cachepos (dir
) = 0;
1979 d_cachepos (dir
) += buf
->NextEntryOffset
;
1980 if ((dir
->__flags
& dirent_get_d_ino
))
1982 FileName
= buf
->FileName
;
1983 FileNameLength
= buf
->FileNameLength
;
1984 FileAttributes
= buf
->FileAttributes
;
1985 if ((dir
->__flags
& dirent_set_d_ino
))
1986 de
->d_ino
= buf
->FileId
.QuadPart
;
1988 else if ((dir
->__flags
& dirent_nfs_d_ino
))
1990 FileName
= ((PFILE_NAMES_INFORMATION
) buf
)->FileName
;
1991 FileNameLength
= ((PFILE_NAMES_INFORMATION
) buf
)->FileNameLength
;
1995 FileName
= ((PFILE_BOTH_DIRECTORY_INFORMATION
) buf
)->FileName
;
1997 ((PFILE_BOTH_DIRECTORY_INFORMATION
) buf
)->FileNameLength
;
1999 ((PFILE_BOTH_DIRECTORY_INFORMATION
) buf
)->FileAttributes
;
2001 RtlInitCountedUnicodeString (&fname
, FileName
, FileNameLength
);
2002 de
->d_ino
= d_mounts (dir
)->check_mount (&fname
, de
->d_ino
);
2003 if (de
->d_ino
== 0 && (dir
->__flags
& dirent_set_d_ino
))
2005 /* Don't try to optimize relative to dir->__d_position. On several
2006 filesystems it's no safe bet that "." and ".." entries always
2008 if (FileNameLength
== sizeof (WCHAR
) && FileName
[0] == '.')
2009 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2010 else if (FileNameLength
== 2 * sizeof (WCHAR
)
2011 && FileName
[0] == L
'.' && FileName
[1] == L
'.')
2013 if (!(dir
->__flags
& dirent_isroot
))
2014 de
->d_ino
= readdir_get_ino (get_name (), true);
2016 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2020 OBJECT_ATTRIBUTES attr
;
2024 InitializeObjectAttributes (&attr
, &fname
,
2025 pc
.objcaseinsensitive (),
2026 get_handle (), NULL
);
2027 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2028 NtOpenFile here returns the inode number of the symlink target,
2029 rather than the inode number of the symlink itself.
2031 Worse, trying to open a symlink without setting the special
2032 "ActOnSymlink" EA triggers a bug in Windows 7 which results
2033 in a timeout of up to 20 seconds, followed by two exceptions
2036 Since both results are far from desirable, we open symlinks
2037 on NFS so that we get the right inode and a happy W7.
2038 And, since some filesystems choke on the EAs, we don't
2039 use them unconditionally. */
2040 f_status
= (dir
->__flags
& dirent_nfs_d_ino
)
2041 ? NtCreateFile (&hdl
, READ_CONTROL
, &attr
, &io
,
2042 NULL
, 0, FILE_SHARE_VALID_FLAGS
,
2043 FILE_OPEN
, FILE_OPEN_FOR_BACKUP_INTENT
,
2044 &nfs_aol_ffei
, sizeof nfs_aol_ffei
)
2045 : NtOpenFile (&hdl
, READ_CONTROL
, &attr
, &io
,
2046 FILE_SHARE_VALID_FLAGS
,
2047 FILE_OPEN_FOR_BACKUP_INTENT
2048 | FILE_OPEN_REPARSE_POINT
);
2049 if (NT_SUCCESS (f_status
))
2051 /* We call NtQueryInformationFile here, rather than
2052 pc.get_ino_by_handle(), otherwise we can't short-circuit
2053 dirent_set_d_ino correctly. */
2054 FILE_INTERNAL_INFORMATION fai
;
2055 f_status
= NtQueryInformationFile (hdl
, &io
, &fai
, sizeof fai
,
2056 FileInternalInformation
);
2058 if (NT_SUCCESS (f_status
))
2060 if (pc
.isgood_inode (fai
.FileId
.QuadPart
))
2061 de
->d_ino
= fai
.FileId
.QuadPart
;
2063 /* Untrusted file system. Don't try to fetch inode
2065 dir
->__flags
&= ~dirent_set_d_ino
;
2072 if (!(res
= readdir_helper (dir
, de
, RtlNtStatusToDosError (status
),
2073 FileAttributes
, &fname
)))
2074 dir
->__d_position
++;
2075 else if (!(dir
->__flags
& dirent_saw_dot
))
2077 strcpy (de
->d_name
, ".");
2078 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2079 de
->d_type
= DT_DIR
;
2080 dir
->__d_position
++;
2081 dir
->__flags
|= dirent_saw_dot
;
2084 else if (!(dir
->__flags
& dirent_saw_dot_dot
))
2086 strcpy (de
->d_name
, "..");
2087 if (!(dir
->__flags
& dirent_isroot
))
2088 de
->d_ino
= readdir_get_ino (get_name (), true);
2090 de
->d_ino
= pc
.get_ino_by_handle (get_handle ());
2091 de
->d_type
= DT_DIR
;
2092 dir
->__d_position
++;
2093 dir
->__flags
|= dirent_saw_dot_dot
;
2097 syscall_printf ("%d = readdir (%p, %p) (L\"%lS\" > \"%ls\") (attr %p > type %d)",
2098 res
, dir
, &de
, res
? NULL
: &fname
, res
? "***" : de
->d_name
,
2099 FileAttributes
, de
->d_type
);
2104 fhandler_disk_file::telldir (DIR *dir
)
2106 return dir
->__d_position
;
2110 fhandler_disk_file::seekdir (DIR *dir
, long loc
)
2113 while (loc
> dir
->__d_position
)
2114 if (!::readdir (dir
))
2119 fhandler_disk_file::rewinddir (DIR *dir
)
2121 d_cachepos (dir
) = 0;
2122 if (wincap
.has_buggy_restart_scan () && isremote ())
2124 /* This works around a W2K bug. The RestartScan parameter in calls
2125 to NtQueryDirectoryFile on remote shares is ignored, thus
2126 resulting in not being able to rewind on remote shares. By
2127 reopening the directory, we get a fresh new directory pointer. */
2128 OBJECT_ATTRIBUTES attr
;
2133 pc
.init_reopen_attr (&attr
, get_handle ());
2134 status
= NtOpenFile (&new_dir
, SYNCHRONIZE
| FILE_LIST_DIRECTORY
,
2135 &attr
, &io
, FILE_SHARE_VALID_FLAGS
,
2136 FILE_SYNCHRONOUS_IO_NONALERT
2137 | FILE_OPEN_FOR_BACKUP_INTENT
2138 | FILE_DIRECTORY_FILE
);
2139 if (!NT_SUCCESS (stat
))
2140 debug_printf ("Unable to reopen dir %s, NT error: %p",
2141 get_name (), status
);
2144 NtClose (get_handle ());
2145 set_io_handle (new_dir
);
2148 dir
->__d_position
= 0;
2149 d_mounts (dir
)->rewind ();
2153 fhandler_disk_file::closedir (DIR *dir
)
2158 delete d_mounts (dir
);
2161 else if (get_handle () == INVALID_HANDLE_VALUE
)
2166 else if (!NT_SUCCESS (status
= NtClose (get_handle ())))
2168 __seterrno_from_nt_status (status
);
2171 syscall_printf ("%d = closedir (%p, %s)", res
, dir
, get_name ());
2175 fhandler_cygdrive::fhandler_cygdrive () :
2176 fhandler_disk_file (), ndrives (0), pdrive (NULL
)
2181 fhandler_cygdrive::open (int flags
, mode_t mode
)
2183 if ((flags
& (O_CREAT
| O_EXCL
)) == (O_CREAT
| O_EXCL
))
2188 if (flags
& O_WRONLY
)
2200 fhandler_cygdrive::close ()
2206 fhandler_cygdrive::set_drives ()
2208 pdrive
= pdrive_buf
;
2209 ndrives
= GetLogicalDriveStrings (sizeof pdrive_buf
, pdrive_buf
) / DRVSZ
;
2213 fhandler_cygdrive::fstat (struct __stat64
*buf
)
2215 fhandler_base::fstat (buf
);
2217 buf
->st_mode
= S_IFDIR
| STD_RBITS
| STD_XBITS
;
2220 char flptst
[] = "X:";
2222 for (const char *p
= pdrive
; p
&& *p
; p
= strchr (p
, '\0') + 1)
2223 if (is_floppy ((flptst
[0] = *p
, flptst
))
2224 || GetFileAttributes (p
) == INVALID_FILE_ATTRIBUTES
)
2226 buf
->st_nlink
= n
+ 2;
2231 fhandler_cygdrive::opendir (int fd
)
2235 dir
= fhandler_disk_file::opendir (fd
);
2236 if (dir
&& !ndrives
)
2243 fhandler_cygdrive::readdir (DIR *dir
, dirent
*de
)
2245 char flptst
[] = "X:";
2249 if (!pdrive
|| !*pdrive
)
2251 if (!(dir
->__flags
& dirent_saw_dot
))
2253 de
->d_name
[0] = '.';
2254 de
->d_name
[1] = '\0';
2259 if (!is_floppy ((flptst
[0] = *pdrive
, flptst
))
2260 && GetFileAttributes (pdrive
) != INVALID_FILE_ATTRIBUTES
)
2262 pdrive
= strchr (pdrive
, '\0') + 1;
2264 *de
->d_name
= cyg_tolower (*pdrive
);
2265 de
->d_name
[1] = '\0';
2266 user_shared
->warned_msdos
= true;
2267 de
->d_ino
= readdir_get_ino (pdrive
, false);
2268 dir
->__d_position
++;
2269 pdrive
= strchr (pdrive
, '\0') + 1;
2270 syscall_printf ("%p = readdir (%p) (%s)", &de
, dir
, de
->d_name
);
2275 fhandler_cygdrive::rewinddir (DIR *dir
)
2277 pdrive
= pdrive_buf
;
2278 dir
->__d_position
= 0;
2282 fhandler_cygdrive::closedir (DIR *dir
)
2284 pdrive
= pdrive_buf
;