1 /* fhandler_disk_file.cc
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008 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 */
83 RtlInitUnicodeString (&proc, L"proc");
84 if (RtlEqualUnicodeString (fname, &proc, TRUE))
86 found[__DIR_PROC] = true;
89 if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2
90 && RtlEqualUnicodeString (fname, &cygdrive, TRUE))
92 found[__DIR_CYGDRIVE] = true;
96 for (int i = 0; i < count; ++i)
97 if (RtlEqualUnicodeString (fname, &mounts[i], TRUE))
100 return eval ? eval_ino (i) : 1;
104 __ino64_t check_missing_mount (PUNICODE_STRING retname = NULL)
106 for (int i = 0; i < count; ++i)
112 *retname = mounts[i];
117 if (parent_dir_len == 1) /* root dir */
119 if (!found[__DIR_PROC])
121 found[__DIR_PROC] = true;
123 RtlInitUnicodeString (retname, L"proc");
126 if (!found[__DIR_CYGDRIVE])
128 found[__DIR_CYGDRIVE] = true;
129 if (cygdrive.Length > 0)
139 void rewind () { memset (found, 0, sizeof found); }
143 is_volume_mountpoint (POBJECT_ATTRIBUTES attr)
149 if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, attr, &io,
150 FILE_SHARE_VALID_FLAGS,
151 FILE_OPEN_FOR_BACKUP_INTENT
152 | FILE_OPEN_REPARSE_POINT)))
154 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER)
155 alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
156 if (NT_SUCCESS (NtFsControlFile (reph, NULL, NULL, NULL,
157 &io, FSCTL_GET_REPARSE_POINT, NULL, 0,
158 (LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE))
159 && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT
160 && rp->SymbolicLinkReparseBuffer.PrintNameLength == 0)
167 static inline __ino64_t
168 get_ino_by_handle (HANDLE hdl)
171 FILE_INTERNAL_INFORMATION fai;
173 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
174 FileInternalInformation)))
175 return fai.FileId.QuadPart;
180 path_conv::ndisk_links (DWORD nNumberOfLinks)
182 if (!isdir () || isremote ())
183 return nNumberOfLinks;
185 OBJECT_ATTRIBUTES attr;
189 if (!NT_SUCCESS (NtOpenFile (&fh, SYNCHRONIZE | FILE_LIST_DIRECTORY,
190 get_object_attr (attr, sec_none_nih),
191 &io, FILE_SHARE_VALID_FLAGS,
192 FILE_SYNCHRONOUS_IO_NONALERT
193 | FILE_OPEN_FOR_BACKUP_INTENT
194 | FILE_DIRECTORY_FILE)))
195 return nNumberOfLinks;
199 PFILE_DIRECTORY_INFORMATION fdibuf = (PFILE_DIRECTORY_INFORMATION)
201 __DIR_mounts *dir = new __DIR_mounts (normalized_path);
202 while (NT_SUCCESS (NtQueryDirectoryFile (fh, NULL, NULL, 0, &io, fdibuf,
203 65536, FileDirectoryInformation,
204 FALSE, NULL, first)))
209 /* All directories have . and .. as their first entries.
210 If . is not present as first entry, we're on a drive's
211 root direcotry, which doesn't have these entries. */
212 if (fdibuf->FileNameLength != 2 || fdibuf->FileName[0] != L'.')
215 for (PFILE_DIRECTORY_INFORMATION pfdi = fdibuf;
217 pfdi = (PFILE_DIRECTORY_INFORMATION)
218 (pfdi->NextEntryOffset ? (PBYTE) pfdi + pfdi->NextEntryOffset
221 switch (pfdi->FileAttributes
222 & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
224 case FILE_ATTRIBUTE_DIRECTORY:
225 /* Just a directory */
228 case FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT:
229 /* Volume mount point or symlink to directory */
231 UNICODE_STRING fname;
233 RtlInitCountedUnicodeString (&fname, pfdi->FileName,
234 pfdi->FileNameLength);
235 InitializeObjectAttributes (&attr, &fname,
236 OBJ_CASE_INSENSITIVE, fh, NULL);
237 if (is_volume_mountpoint (&attr))
244 UNICODE_STRING fname;
245 RtlInitCountedUnicodeString (&fname, pfdi->FileName,
246 pfdi->FileNameLength);
247 dir->check_mount (&fname, 0, false);
250 while (dir->check_missing_mount ())
258 path_conv::isgood_inode (__ino64_t ino) const
260 /* We can't trust remote inode numbers of only 32 bit. That means,
261 all remote inode numbers when running under NT4, as well as remote NT4
262 NTFS, as well as shares of Samba version < 3.0.
263 The known exception are SFU NFS shares, which return the valid 32 bit
264 inode number from the remote file system unchanged. */
265 return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ());
268 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
269 This returns the content of a struct fattr3 as defined in RFC 1813.
270 The content is the NFS equivalent of struct stat. so there's not much
271 to do here except for copying. */
273 fhandler_base::fstat_by_nfs_ea (struct __stat64 *buf)
278 FILE_FULL_EA_INFORMATION ffei;
279 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
282 FILE_GET_EA_INFORMATION fgei;
283 char buf[sizeof (NFS_V3_ATTR)];
286 fgei_buf.fgei.NextEntryOffset = 0;
287 fgei_buf.fgei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
288 stpcpy (fgei_buf.fgei.EaName, NFS_V3_ATTR);
289 status = NtQueryEaFile (get_handle (), &io,
290 &ffei_buf.ffei, sizeof ffei_buf, TRUE,
291 &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
292 if (NT_SUCCESS (status))
294 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
295 + ffei_buf.ffei.EaNameLength + 1);
296 buf->st_dev = nfs_attr->fsid;
297 buf->st_ino = nfs_attr->fileid;
298 buf->st_mode = (nfs_attr->mode & 0xfff)
299 | nfs_type_mapping[nfs_attr->type & 7];
300 buf->st_nlink = nfs_attr->nlink;
301 /* FIXME: How to convert UNIX uid/gid to Windows SIDs? */
303 buf->st_uid = nfs_attr->uid;
304 buf->st_gid = nfs_attr->gid;
306 buf->st_uid = myself->uid;
307 buf->st_gid = myself->gid;
309 buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
310 nfs_attr->rdev.specdata2);
311 buf->st_size = nfs_attr->size;
312 buf->st_blksize = PREFERRED_IO_BLKSIZE;
313 buf->st_blocks = nfs_attr->used / 512;
314 buf->st_atim = nfs_attr->atime;
315 buf->st_mtim = nfs_attr->mtime;
316 buf->st_ctim = nfs_attr->ctime;
319 debug_printf ("%p = NtQueryEaFile(%S)", status, pc.get_nt_native_path ());
324 fhandler_base::fstat_by_handle (struct __stat64 *buf)
330 return fstat_by_nfs_ea (buf);
333 FILE_ALL_INFORMATION fai;
334 WCHAR buf[NAME_MAX + 1];
337 status = NtQueryInformationFile (get_handle (), &io, &fai_buf.fai,
338 sizeof fai_buf, FileAllInformation);
339 if (!NT_SUCCESS (status))
341 debug_printf ("%p = NtQueryInformationFile(%S)",
342 status, pc.get_nt_native_path ());
345 /* If the change time is 0, it's a file system which doesn't
346 support a change timestamp. In that case use the LastWriteTime
347 entry, as in other calls to fstat_helper. */
348 if (pc.is_rep_symlink ())
349 fai_buf.fai.BasicInformation.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
350 pc.file_attributes (fai_buf.fai.BasicInformation.FileAttributes);
351 return fstat_helper (buf,
352 fai_buf.fai.BasicInformation.ChangeTime.QuadPart
353 ? *(FILETIME *) &fai_buf.fai.BasicInformation.ChangeTime
354 : *(FILETIME *) &fai_buf.fai.BasicInformation.LastWriteTime,
355 *(FILETIME *) &fai_buf.fai.BasicInformation.LastAccessTime,
356 *(FILETIME *) &fai_buf.fai.BasicInformation.LastWriteTime,
357 *(FILETIME *) &fai_buf.fai.BasicInformation.CreationTime,
359 fai_buf.fai.StandardInformation.EndOfFile.QuadPart,
360 fai_buf.fai.StandardInformation.AllocationSize.QuadPart,
361 fai_buf.fai.InternalInformation.FileId.QuadPart,
362 fai_buf.fai.StandardInformation.NumberOfLinks,
363 fai_buf.fai.BasicInformation.FileAttributes);
367 fhandler_base::fstat_by_name (struct __stat64 *buf)
370 OBJECT_ATTRIBUTES attr;
372 UNICODE_STRING dirname;
373 UNICODE_STRING basename;
376 FILE_ID_BOTH_DIR_INFORMATION fdi;
377 WCHAR buf[NAME_MAX + 1];
379 LARGE_INTEGER FileId;
381 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
382 InitializeObjectAttributes (&attr, &dirname, OBJ_CASE_INSENSITIVE,
384 if (!NT_SUCCESS (status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
385 &attr, &io, FILE_SHARE_VALID_FLAGS,
386 FILE_SYNCHRONOUS_IO_NONALERT
387 | FILE_OPEN_FOR_BACKUP_INTENT
388 | FILE_DIRECTORY_FILE)))
390 debug_printf ("%p = NtOpenFile(%S)", status, pc.get_nt_native_path ());
393 if (wincap.has_fileid_dirinfo ()
394 && NT_SUCCESS (status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
395 &fdi_buf.fdi, sizeof fdi_buf,
396 FileIdBothDirectoryInformation,
397 TRUE, &basename, TRUE)))
398 FileId = fdi_buf.fdi.FileId;
399 else if (NT_SUCCESS (status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
400 &fdi_buf.fdi, sizeof fdi_buf,
401 FileDirectoryInformation,
402 TRUE, &basename, TRUE)))
403 FileId.QuadPart = 0; /* get_ino is called in fstat_helper. */
404 if (!NT_SUCCESS (status))
406 debug_printf ("%p = NtQueryDirectoryFile(%S)", status,
407 pc.get_nt_native_path ());
412 /* If the change time is 0, it's a file system which doesn't
413 support a change timestamp. In that case use the LastWriteTime
414 entry, as in other calls to fstat_helper. */
415 if (pc.is_rep_symlink ())
416 fdi_buf.fdi.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
417 pc.file_attributes (fdi_buf.fdi.FileAttributes);
418 return fstat_helper (buf,
419 fdi_buf.fdi.ChangeTime.QuadPart ?
420 *(FILETIME *) &fdi_buf.fdi.ChangeTime :
421 *(FILETIME *) &fdi_buf.fdi.LastWriteTime,
422 *(FILETIME *) &fdi_buf.fdi.LastAccessTime,
423 *(FILETIME *) &fdi_buf.fdi.LastWriteTime,
424 *(FILETIME *) &fdi_buf.fdi.CreationTime,
425 pc.fs_serial_number (),
426 fdi_buf.fdi.EndOfFile.QuadPart,
427 fdi_buf.fdi.AllocationSize.QuadPart,
430 fdi_buf.fdi.FileAttributes);
434 /* Arbitrary value: 2006-12-01 */
435 RtlSecondsSince1970ToTime (1164931200L, &ft);
436 return fstat_helper (buf,
446 pc.file_attributes ());
450 fhandler_base::fstat_fs (struct __stat64 *buf)
454 int open_flags = O_RDONLY | O_BINARY;
458 if (!nohandle () && !is_fs_special ())
459 res = fstat_by_handle (buf);
461 res = fstat_by_name (buf);
464 query_open (query_read_attributes);
465 oret = open_fs (open_flags, 0);
468 /* We now have a valid handle, regardless of the "nohandle" state.
469 Since fhandler_base::open only calls CloseHandle if !nohandle,
470 we have to set it to false before calling close and restore
471 the state afterwards. */
472 res = fstat_by_handle (buf);
473 bool no_handle = nohandle ();
476 nohandle (no_handle);
477 set_io_handle (NULL);
480 res = fstat_by_name (buf);
485 /* The ftChangeTime is taken from the NTFS ChangeTime entry, if reading
486 the file information using NtQueryInformationFile succeeded. If not,
487 it's faked using the LastWriteTime entry from GetFileInformationByHandle
488 or FindFirstFile. We're deliberatly not using the creation time anymore
489 to simplify interaction with native Windows applications which choke on
490 creation times >= access or write times.
492 Note that the dwFileAttributes member of the file information evaluated
493 in the calling function is used here, not the pc.fileattr member, since
494 the latter might be old and not reflect the actual state of the file. */
496 fhandler_base::fstat_helper (struct __stat64 *buf,
497 FILETIME ftChangeTime,
498 FILETIME ftLastAccessTime,
499 FILETIME ftLastWriteTime,
500 FILETIME ftCreationTime,
501 DWORD dwVolumeSerialNumber,
504 ULONGLONG nFileIndex,
505 DWORD nNumberOfLinks,
506 DWORD dwFileAttributes)
509 FILE_COMPRESSION_INFORMATION fci;
511 to_timestruc_t (&ftLastAccessTime, &buf->st_atim);
512 to_timestruc_t (&ftLastWriteTime, &buf->st_mtim);
513 to_timestruc_t (&ftChangeTime, &buf->st_ctim);
514 to_timestruc_t (&ftCreationTime, &buf->st_birthtim);
515 buf->st_dev = dwVolumeSerialNumber;
516 buf->st_size = (_off64_t) nFileSize;
517 /* The number of links to a directory includes the
518 number of subdirectories in the directory, since all
519 those subdirectories point to it.
520 This is too slow on remote drives, so we do without it.
521 Setting the count to 2 confuses `find (1)' command. So
522 let's try it with `1' as link count. */
524 buf->st_nlink = pc.ndisk_links (nNumberOfLinks);
526 buf->st_nlink = nNumberOfLinks;
529 /* Enforce namehash as inode number on untrusted file systems. */
530 if (pc.isgood_inode (nFileIndex))
531 buf->st_ino = (__ino64_t) nFileIndex;
533 buf->st_ino = get_ino ();
535 buf->st_blksize = PREFERRED_IO_BLKSIZE;
537 if (nAllocSize >= 0LL)
538 /* A successful NtQueryInformationFile returns the allocation size
539 correctly for compressed and sparse files as well. */
540 buf->st_blocks = (nAllocSize + S_BLKSIZE - 1) / S_BLKSIZE;
541 else if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_COMPRESSED
542 | FILE_ATTRIBUTE_SPARSE_FILE)
543 && get_handle () && !is_fs_special ()
544 && !NtQueryInformationFile (get_handle (), &st, (PVOID) &fci,
545 sizeof fci, FileCompressionInformation))
546 /* Otherwise we request the actual amount of bytes allocated for
547 compressed and sparsed files. */
548 buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
551 /* Otherwise compute no. of blocks from file size. */
552 buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
555 /* Using a side effect: get_file_attibutes checks for
556 directory. This is used, to set S_ISVTX, if needed. */
558 buf->st_mode = S_IFDIR;
559 else if (pc.issymlink ())
561 buf->st_size = pc.get_symlink_length ();
562 /* symlinks are everything for everyone! */
563 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
564 get_file_attribute (get_handle (), pc, NULL,
565 &buf->st_uid, &buf->st_gid);
568 else if (pc.issocket ())
569 buf->st_mode = S_IFSOCK;
571 if (!get_file_attribute (is_fs_special () && !pc.issocket ()
572 ? NULL : get_handle (), pc,
573 &buf->st_mode, &buf->st_uid, &buf->st_gid))
575 /* If read-only attribute is set, modify ntsec return value */
576 if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY)
577 && !pc.isdir () && !pc.issymlink ())
578 buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
580 if (buf->st_mode & S_IFMT)
582 else if (!is_fs_special ())
583 buf->st_mode |= S_IFREG;
586 buf->st_dev = dev ();
587 buf->st_mode = dev ().mode;
593 buf->st_mode |= STD_RBITS;
595 if (!::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY))
596 buf->st_mode |= STD_WBITS;
597 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
600 buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
601 else if (buf->st_mode & S_IFMT)
603 else if (is_fs_special ())
605 buf->st_dev = dev ();
606 buf->st_mode = dev ().mode;
611 buf->st_mode |= S_IFREG;
612 if (pc.exec_state () == dont_know_if_executable)
615 OBJECT_ATTRIBUTES attr;
619 RtlInitUnicodeString (&same, L"");
620 InitializeObjectAttributes (&attr, &same, 0, get_handle (), NULL);
621 if (NT_SUCCESS (NtOpenFile (&h, FILE_READ_DATA, &attr, &io,
622 FILE_SHARE_VALID_FLAGS, 0)))
624 LARGE_INTEGER off = { QuadPart:0LL };
627 if (NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL, &io, magic,
629 && has_exec_chars (magic, io.Information))
632 buf->st_mode |= STD_XBITS;
638 if (pc.exec_state () == is_executable)
639 buf->st_mode |= STD_XBITS;
641 /* This fakes the permissions of all files to match the current umask. */
642 buf->st_mode &= ~(cygheap->umask);
646 syscall_printf ("0 = fstat (, %p) st_atime=%x st_size=%D, st_mode=%p, st_ino=%D, sizeof=%d",
647 buf, buf->st_atime, buf->st_size, buf->st_mode,
648 buf->st_ino, sizeof (*buf));
653 fhandler_disk_file::fstat (struct __stat64 *buf)
655 return fstat_fs (buf);
659 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
661 int ret = -1, opened = 0;
664 FILE_FS_FULL_SIZE_INFORMATION full_fsi;
665 FILE_FS_SIZE_INFORMATION fsi;
666 HANDLE fh = get_handle ();
670 OBJECT_ATTRIBUTES attr;
671 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
672 pc.get_object_attr (attr, sec_none_nih),
673 &io, FILE_SHARE_VALID_FLAGS,
674 FILE_OPEN_FOR_BACKUP_INTENT));
677 /* Can't open file. Try again with parent dir. */
678 UNICODE_STRING dirname;
679 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
680 attr.ObjectName = &dirname;
681 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
682 FILE_SHARE_VALID_FLAGS,
683 FILE_OPEN_FOR_BACKUP_INTENT));
689 sfs->f_files = ULONG_MAX;
690 sfs->f_ffree = ULONG_MAX;
691 sfs->f_favail = ULONG_MAX;
692 sfs->f_fsid = pc.fs_serial_number ();
693 sfs->f_flag = pc.fs_flags ();
694 sfs->f_namemax = pc.fs_name_len ();
695 /* Get allocation related information. Try to get "full" information
696 first, which is only available since W2K. If that fails, try to
697 retrieve normal allocation information. */
698 status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
699 FileFsFullSizeInformation);
700 if (NT_SUCCESS (status))
702 sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
703 sfs->f_frsize = sfs->f_bsize;
704 sfs->f_blocks = full_fsi.TotalAllocationUnits.LowPart;
705 sfs->f_bfree = full_fsi.ActualAvailableAllocationUnits.LowPart;
706 sfs->f_bavail = full_fsi.CallerAvailableAllocationUnits.LowPart;
707 if (sfs->f_bfree > sfs->f_bavail)
709 /* Quotas active. We can't trust TotalAllocationUnits. */
710 NTFS_VOLUME_DATA_BUFFER nvdb;
712 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
713 FSCTL_GET_NTFS_VOLUME_DATA,
714 NULL, 0, &nvdb, sizeof nvdb);
715 if (!NT_SUCCESS (status))
716 debug_printf ("%p = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
717 status, pc.get_nt_native_path ());
719 sfs->f_blocks = nvdb.TotalClusters.QuadPart;
725 status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
726 FileFsSizeInformation);
727 if (!NT_SUCCESS (status))
729 __seterrno_from_nt_status (status);
732 sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
733 sfs->f_frsize = sfs->f_bsize;
734 sfs->f_blocks = fsi.TotalAllocationUnits.LowPart;
735 sfs->f_bfree = fsi.AvailableAllocationUnits.LowPart;
736 sfs->f_bavail = sfs->f_bfree;
742 syscall_printf ("%d = fstatvfs (%s, %p)", ret, get_name (), sfs);
747 fhandler_disk_file::fchmod (mode_t mode)
749 extern int chmod_device (path_conv& pc, mode_t mode);
755 if (pc.is_fs_special ())
756 return chmod_device (pc, mode);
760 query_open (query_write_control);
761 if (!(oret = open (O_BINARY, 0)))
763 /* Need WRITE_DAC|WRITE_OWNER to write ACLs. */
766 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
767 query_open (query_write_attributes);
768 if (!(oret = open (O_BINARY, 0)))
775 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
776 Only type and mode have to be set. Apparently type isn't checked
777 for consistency, so it's sufficent to set it to NF3REG all the time. */
779 FILE_FULL_EA_INFORMATION ffei;
780 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
782 ffei_buf.ffei.NextEntryOffset = 0;
783 ffei_buf.ffei.Flags = 0;
784 ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
785 ffei_buf.ffei.EaValueLength = sizeof (fattr3);
786 strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
787 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
788 + ffei_buf.ffei.EaNameLength + 1);
789 memset (nfs_attr, 0, sizeof (fattr3));
790 nfs_attr->type = NF3REG;
791 nfs_attr->mode = mode;
792 status = NtSetEaFile (get_handle (), &io,
793 &ffei_buf.ffei, sizeof ffei_buf);
794 if (!NT_SUCCESS (status))
795 __seterrno_from_nt_status (status);
805 if (!set_file_attribute (get_handle (), pc,
806 ILLEGAL_UID, ILLEGAL_GID, mode))
810 /* if the mode we want has any write bits set, we can't be read only. */
811 if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
812 pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
814 pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
816 pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
818 FILE_BASIC_INFORMATION fbi;
819 fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart
820 = fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
821 fbi.FileAttributes = pc.file_attributes () ?: FILE_ATTRIBUTE_NORMAL;
822 status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
823 FileBasicInformation);
824 /* Correct NTFS security attributes have higher priority */
827 if (!NT_SUCCESS (status))
828 __seterrno_from_nt_status (status);
841 fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
847 /* fake - if not supported, pretend we're like win95
848 where it just works */
849 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
855 query_open (query_write_control);
856 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
863 int res = get_file_attribute (get_handle (), pc, &attrib, NULL, NULL);
866 /* Typical Windows default ACLs can contain permissions for one
867 group, while being owned by another user/group. The permission
868 bits returned above are pretty much useless then. Creating a
869 new ACL with these useless permissions results in a potentially
870 broken symlink. So what we do here is to set the underlying
871 permissions of symlinks to a sensible value which allows the
872 world to read the symlink and only the new owner to change it. */
874 attrib = S_IFLNK | STD_RBITS | STD_WBITS;
875 res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
884 fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp)
897 /* Open for writing required to be able to set ctime
898 (even though setting the ACL is just pretended). */
900 oret = open (O_WRONLY | O_BINARY, 0);
906 else if (nentries < MIN_ACL_ENTRIES)
912 query_open (query_read_attributes);
913 oret = open (O_BINARY, 0);
915 if ((!oret && !fstat_by_handle (&st))
916 || !fstat_by_name (&st))
918 aclbufp[0].a_type = USER_OBJ;
919 aclbufp[0].a_id = st.st_uid;
920 aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
921 aclbufp[1].a_type = GROUP_OBJ;
922 aclbufp[1].a_id = st.st_gid;
923 aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
924 aclbufp[2].a_type = OTHER_OBJ;
925 aclbufp[2].a_id = ILLEGAL_GID;
926 aclbufp[2].a_perm = st.st_mode & S_IRWXO;
927 aclbufp[3].a_type = CLASS_OBJ;
928 aclbufp[3].a_id = ILLEGAL_GID;
929 aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
930 res = MIN_ACL_ENTRIES;
935 res = MIN_ACL_ENTRIES;
946 query_open (cmd == SETACL ? query_write_control : query_read_control);
947 if (!(oret = open (O_BINARY, 0)))
949 if (cmd == GETACL || cmd == GETACLCNT)
950 goto cant_access_acl;
957 if (!aclsort32 (nentries, 0, aclbufp))
960 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
964 FILE_BASIC_INFORMATION fbi;
965 fbi.CreationTime.QuadPart
966 = fbi.LastAccessTime.QuadPart
967 = fbi.LastWriteTime.QuadPart
968 = fbi.ChangeTime.QuadPart = 0LL;
969 fbi.FileAttributes = (pc.file_attributes ()
970 & ~FILE_ATTRIBUTE_READONLY)
971 ?: FILE_ATTRIBUTE_NORMAL;
972 NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
973 FileBasicInformation);
981 res = getacl (get_handle (), pc, nentries, aclbufp);
984 res = getacl (get_handle (), pc, 0, NULL);
999 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1001 if (pc.is_fs_special ())
1003 set_errno (ENOTSUP);
1006 return read_ea (get_handle (), pc, name, (char *) value, size);
1010 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1013 if (pc.is_fs_special ())
1015 set_errno (ENOTSUP);
1018 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1022 fhandler_disk_file::fadvise (_off64_t offset, _off64_t length, int advice)
1024 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1030 /* Windows only supports advice flags for the whole file. We're using
1031 a simplified test here so that we don't have to ask for the actual
1032 file size. Length == 0 means all bytes starting at offset anyway.
1033 So we only actually follow the advice, if it's given for offset == 0. */
1037 /* We only support normal and sequential mode for now. Everything which
1038 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1039 if (advice != POSIX_FADV_SEQUENTIAL)
1040 advice = POSIX_FADV_NORMAL;
1043 FILE_MODE_INFORMATION fmi;
1044 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1046 FileModeInformation);
1047 if (!NT_SUCCESS (status))
1048 __seterrno_from_nt_status (status);
1051 fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1052 if (advice == POSIX_FADV_SEQUENTIAL)
1053 fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1054 status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1055 FileModeInformation);
1056 if (NT_SUCCESS (status))
1058 __seterrno_from_nt_status (status);
1065 fhandler_disk_file::ftruncate (_off64_t length, bool allow_truncate)
1069 if (length < 0 || !get_handle ())
1071 else if (pc.isdir ())
1073 else if (!(get_access () & GENERIC_WRITE))
1079 FILE_STANDARD_INFORMATION fsi;
1080 FILE_END_OF_FILE_INFORMATION feofi;
1082 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1083 FileStandardInformation);
1084 if (!NT_SUCCESS (status))
1086 __seterrno_from_nt_status (status);
1090 /* If called through posix_fallocate, silently succeed if length
1091 is less than the file's actual length. */
1092 if (!allow_truncate && length < fsi.EndOfFile.QuadPart)
1095 feofi.EndOfFile.QuadPart = length;
1096 /* Create sparse files only when called through ftruncate, not when
1097 called through posix_fallocate. */
1099 && (pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES)
1100 && length >= fsi.EndOfFile.QuadPart + (128 * 1024))
1102 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1103 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1104 syscall_printf ("%p = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1105 status, pc.get_nt_native_path ());
1107 status = NtSetInformationFile (get_handle (), &io,
1108 &feofi, sizeof feofi,
1109 FileEndOfFileInformation);
1110 if (!NT_SUCCESS (status))
1111 __seterrno_from_nt_status (status);
1119 fhandler_disk_file::link (const char *newpath)
1121 extern bool allow_winsymlinks;
1123 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
1126 set_errno (newpc.case_clash ? ECASECLASH : newpc.error);
1130 if (newpc.exists ())
1132 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1137 char new_buf[strlen (newpath) + 5];
1138 if (!newpc.error && !newpc.case_clash)
1140 if (allow_winsymlinks && pc.is_lnk_special ())
1142 /* Shortcut hack. */
1143 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1145 newpc.check (newpath, PC_SYM_NOFOLLOW);
1147 else if (!pc.isdir ()
1149 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1152 /* Executable hack. */
1153 stpcpy (stpcpy (new_buf, newpath), ".exe");
1155 newpc.check (newpath, PC_SYM_NOFOLLOW);
1161 OBJECT_ATTRIBUTES attr;
1163 status = NtOpenFile (&fh, READ_CONTROL,
1164 pc.get_object_attr (attr, sec_none_nih), &io,
1165 FILE_SHARE_VALID_FLAGS,
1166 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
1167 if (!NT_SUCCESS (status))
1169 __seterrno_from_nt_status (status);
1172 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1173 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1174 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1175 pfli->ReplaceIfExists = FALSE;
1176 pfli->RootDirectory = NULL;
1177 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1178 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1180 if (!NT_SUCCESS (status))
1182 if (status == STATUS_INVALID_DEVICE_REQUEST)
1184 /* FS doesn't support hard links. Try to copy file. */
1185 WCHAR pcw[pc.get_nt_native_path ()->Length + 1];
1186 WCHAR newpcw[newpc.get_nt_native_path ()->Length + 1];
1187 if (!CopyFileW (pc.get_wide_win32_path (pcw),
1188 newpc.get_wide_win32_path (newpcw), TRUE))
1193 if (!allow_winsymlinks && pc.is_lnk_special ())
1194 SetFileAttributesW (newpcw, pc.file_attributes ()
1195 | FILE_ATTRIBUTE_SYSTEM
1196 | FILE_ATTRIBUTE_READONLY);
1200 __seterrno_from_nt_status (status);
1208 fhandler_disk_file::utimens (const struct timespec *tvp)
1210 return utimens_fs (tvp);
1214 fhandler_base::utimens_fs (const struct timespec *tvp)
1216 LARGE_INTEGER lastaccess, lastwrite;
1217 struct timespec timeofday;
1218 struct timespec tmp[2];
1219 bool closeit = false;
1223 query_open (query_write_attributes);
1224 if (!open_fs (O_BINARY, 0))
1226 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1227 to change the timestamps. Unfortunately it's not sufficient for a
1228 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1229 for writing, though this fails for R/O files of course. */
1230 query_open (no_query);
1231 if (!open_fs (O_WRONLY | O_BINARY, 0))
1233 syscall_printf ("Opening file failed");
1240 gettimeofday (reinterpret_cast<struct timeval *> (&timeofday), 0);
1241 timeofday.tv_nsec *= 1000;
1243 tmp[1] = tmp[0] = timeofday;
1246 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1247 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1252 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1253 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1255 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1256 timespec_to_filetime (&tmp[0], (FILETIME *) &lastaccess);
1257 timespec_to_filetime (&tmp[1], (FILETIME *) &lastwrite);
1258 debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
1261 FILE_BASIC_INFORMATION fbi;
1262 fbi.CreationTime.QuadPart = 0LL;
1263 fbi.LastAccessTime = lastaccess;
1264 fbi.LastWriteTime = lastwrite;
1265 fbi.ChangeTime.QuadPart = 0LL;
1266 fbi.FileAttributes = 0;
1267 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1268 FileBasicInformation);
1271 /* Opening a directory on a 9x share from a NT machine works(!), but
1272 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1273 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1275 __seterrno_from_nt_status (status);
1281 fhandler_disk_file::fhandler_disk_file () :
1286 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1293 fhandler_disk_file::open (int flags, mode_t mode)
1295 return open_fs (flags, mode);
1299 fhandler_base::open_fs (int flags, mode_t mode)
1301 if (pc.case_clash && flags & O_CREAT)
1303 debug_printf ("case clash detected");
1304 set_errno (ECASECLASH);
1308 /* Unfortunately NT allows to open directories for writing, but that's
1309 disallowed according to SUSv3. */
1310 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1316 int res = fhandler_base::open (flags | O_DIROPEN, mode);
1320 /* This is for file systems known for having a buggy CreateFile call
1321 which might return a valid HANDLE without having actually opened
1323 The only known file system to date is the SUN NFS Solstice Client 3.1
1324 which returns a valid handle when trying to open a file in a nonexistent
1326 if (pc.has_buggy_open () && !pc.exists ())
1328 debug_printf ("Buggy open detected.");
1334 if (pc.hasgood_inode ())
1335 ino = get_ino_by_handle (get_handle ());
1336 /* A unique ID is necessary to recognize fhandler entries which are
1337 duplicated by dup(2) or fork(2). */
1338 AllocateLocallyUniqueId ((PLUID) &unique_id);
1341 syscall_printf ("%d = fhandler_disk_file::open (%S, %p)", res,
1342 pc.get_nt_native_path (), flags);
1347 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
1350 _off64_t curpos = lseek (0, SEEK_CUR);
1351 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1355 size_t tmp_count = count;
1356 read (buf, tmp_count);
1357 if (lseek (curpos, SEEK_SET) >= 0)
1358 res = (ssize_t) tmp_count;
1362 debug_printf ("%d = pread (%p, %d, %d)\n", res, buf, count, offset);
1367 fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
1370 _off64_t curpos = lseek (0, SEEK_CUR);
1371 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1375 res = (ssize_t) write (buf, count);
1376 if (lseek (curpos, SEEK_SET) < 0)
1379 debug_printf ("%d = pwrite (%p, %d, %d)\n", res, buf, count, offset);
1384 fhandler_disk_file::mkdir (mode_t mode)
1387 SECURITY_ATTRIBUTES sa = sec_none_nih;
1388 security_descriptor sd;
1391 set_security_attribute (S_IFDIR | ((mode & 07777) & ~cygheap->umask),
1396 OBJECT_ATTRIBUTES attr;
1398 ULONG fattr = FILE_ATTRIBUTE_DIRECTORY;
1399 PFILE_FULL_EA_INFORMATION p = NULL;
1402 if (pc.fs_is_nfs ())
1404 /* When creating a dir on an NFS share, we have to set the
1405 file mode by writing a NFS fattr3 structure with the
1406 correct mode bits set. */
1407 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1409 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1410 p->NextEntryOffset = 0;
1412 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1413 p->EaValueLength = sizeof (fattr3);
1414 strcpy (p->EaName, NFS_V3_ATTR);
1415 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1416 memset (nfs_attr, 0, sizeof (fattr3));
1417 nfs_attr->type = NF3DIR;
1418 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1420 status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
1421 pc.get_object_attr (attr, sa), &io, NULL,
1422 fattr, FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1423 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1424 | FILE_OPEN_FOR_BACKUP_INTENT,
1426 if (NT_SUCCESS (status))
1432 __seterrno_from_nt_status (status);
1438 fhandler_disk_file::rmdir ()
1440 extern NTSTATUS unlink_nt (path_conv &pc);
1444 set_errno (ENOTDIR);
1453 NTSTATUS status = unlink_nt (pc);
1455 /* Check for existence of remote dirs after trying to delete them.
1457 - Sometimes SMB indicates failure when it really succeeds.
1458 - Removeing a directory on a samba drive doesn't return an error if the
1459 directory can't be removed because it's not empty. */
1462 OBJECT_ATTRIBUTES attr;
1463 FILE_BASIC_INFORMATION fbi;
1465 if (NT_SUCCESS (NtQueryAttributesFile
1466 (pc.get_object_attr (attr, sec_none_nih), &fbi)))
1467 status = STATUS_DIRECTORY_NOT_EMPTY;
1469 status = STATUS_SUCCESS;
1471 if (!NT_SUCCESS (status))
1473 __seterrno_from_nt_status (status);
1479 /* This is the minimal number of entries which fit into the readdir cache.
1480 The number of bytes allocated by the cache is determined by this number,
1481 To tune caching, just tweak this number. To get a feeling for the size,
1482 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1484 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1486 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1487 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1488 + (NAME_MAX + 1) * sizeof (WCHAR)))
1492 char __cache[DIR_BUF_SIZE]; /* W2K needs this buffer 8 byte aligned. */
1496 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1497 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1499 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1502 fhandler_disk_file::opendir (int fd)
1508 set_errno (ENOTDIR);
1509 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1511 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1517 else if ((dir->__d_dirent =
1518 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1526 if (cfd < 0 && fd < 0)
1529 dir->__d_dirent->__d_version = __DIRENT_VERSION;
1530 dir->__d_cookie = __DIRENT_COOKIE;
1531 dir->__handle = INVALID_HANDLE_VALUE;
1532 dir->__d_position = 0;
1533 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1534 ? dirent_isroot : 0;
1535 dir->__d_internal = (unsigned) new __DIR_mounts (get_name ());
1536 d_cachepos (dir) = 0;
1538 if (!pc.iscygdrive ())
1542 /* opendir() case. Initialize with given directory name and
1543 NULL directory handle. */
1544 OBJECT_ATTRIBUTES attr;
1548 status = NtOpenFile (&get_handle (),
1549 SYNCHRONIZE | FILE_LIST_DIRECTORY,
1550 pc.get_object_attr (attr, sec_none_nih),
1551 &io, FILE_SHARE_VALID_FLAGS,
1552 FILE_SYNCHRONOUS_IO_NONALERT
1553 | FILE_OPEN_FOR_BACKUP_INTENT
1554 | FILE_DIRECTORY_FILE);
1555 if (!NT_SUCCESS (status))
1557 __seterrno_from_nt_status (status);
1562 /* FileIdBothDirectoryInformation is apparently unsupported on
1563 XP when accessing directories on UDF. When trying to use it
1564 so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1565 It's not clear if the call isn't also unsupported on other
1566 OS/FS combinations (say, Win2K/CDFS or so). Instead of
1567 testing in readdir for yet another error code, let's use
1568 FileIdBothDirectoryInformation only on filesystems supporting
1569 persistent ACLs, FileDirectoryInformation otherwise.
1571 NFS clients hide dangling symlinks from directory queries,
1572 unless you use the FileNamesInformation info class.
1573 On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
1574 works fine, but only if the NFS share is mounted to a drive
1575 letter. TODO: We don't test that here for now, but it might
1576 be worth to test if there's a speed gain in using
1577 FileIdBothDirectoryInformation, because it doesn't require to
1578 open the file to read the inode number. */
1579 if (pc.hasgood_inode ())
1581 dir->__flags |= dirent_set_d_ino;
1582 if (pc.fs_is_nfs ())
1583 dir->__flags |= dirent_nfs_d_ino;
1584 else if (wincap.has_fileid_dirinfo ())
1585 dir->__flags |= dirent_get_d_ino;
1592 /* Filling cfd with `this' (aka storing this in the file
1593 descriptor table should only happen after it's clear that
1594 opendir doesn't fail, otherwise we end up cfree'ing the
1595 fhandler twice, once in opendir() in dir.cc, the second
1596 time on exit. Nasty, nasty... */
1599 if (pc.iscygdrive ())
1600 cfd->nohandle (true);
1602 set_close_on_exec (true);
1607 syscall_printf ("%p = opendir (%s)", res, get_name ());
1611 delete d_mounts (dir);
1613 free (dir->__d_dirent);
1615 free (dir->__d_dirname);
1622 readdir_get_ino (const char *path, bool dot_dot)
1627 OBJECT_ATTRIBUTES attr;
1633 fname = (char *) alloca (strlen (path) + 4);
1634 char *c = stpcpy (fname, path);
1640 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX);
1641 if (pc.isspecial ())
1643 if (!stat_worker (pc, &st))
1646 else if (!pc.hasgood_inode ())
1647 ino = hash_path_name (0, pc.get_nt_native_path ());
1648 else if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1649 pc.get_object_attr (attr, sec_none_nih),
1650 &io, FILE_SHARE_VALID_FLAGS,
1651 FILE_OPEN_FOR_BACKUP_INTENT
1652 | (pc.is_rep_symlink ()
1653 ? FILE_OPEN_REPARSE_POINT : 0))))
1655 ino = get_ino_by_handle (hdl);
1662 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1663 DWORD attr, PUNICODE_STRING fname)
1668 if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
1671 return geterrno_from_win_error (w32_err);
1674 dir->__flags &= ~dirent_set_d_ino;
1677 /* Check for directory reparse point. These are potential volume mount
1678 points which have another inode than the underlying directory. */
1679 if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1680 == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1683 OBJECT_ATTRIBUTES attr;
1686 InitializeObjectAttributes (&attr, fname, OBJ_CASE_INSENSITIVE,
1687 get_handle (), NULL);
1688 if (is_volume_mountpoint (&attr)
1689 && (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
1690 FILE_SHARE_VALID_FLAGS,
1691 FILE_OPEN_FOR_BACKUP_INTENT))))
1693 de->d_ino = get_ino_by_handle (reph);
1698 /* Check for Windows shortcut. If it's a Cygwin or U/WIN
1699 symlink, drop the .lnk suffix. */
1700 if ((attr & FILE_ATTRIBUTE_READONLY) && fname->Length > 4 * sizeof (WCHAR))
1702 UNICODE_STRING uname;
1703 UNICODE_STRING lname;
1705 RtlInitCountedUnicodeString (&uname,
1707 + fname->Length / sizeof (WCHAR) - 4,
1708 4 * sizeof (WCHAR));
1709 RtlInitUnicodeString (&lname, (PWCHAR) L".lnk");
1710 if (RtlEqualUnicodeString (&uname, &lname, TRUE))
1713 UNICODE_STRING fbuf;
1716 RtlCopyUnicodeString (&fbuf, pc.get_nt_native_path ());
1717 RtlAppendUnicodeToString (&fbuf, L"\\");
1718 RtlAppendUnicodeStringToString (&fbuf, fname);
1719 fbuf.Buffer += 4; /* Skip leading \??\ */
1720 fbuf.Length -= 4 * sizeof (WCHAR);
1721 if (fbuf.Buffer[1] != L':') /* UNC path */
1723 *(fbuf.Buffer += 2) = L'\\';
1724 fbuf.Length -= 2 * sizeof (WCHAR);
1726 path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
1727 if (fpath.issymlink () || fpath.is_fs_special ())
1728 fname->Length -= 4 * sizeof (WCHAR);
1733 if (pc.isencoded ())
1735 char tmp[NAME_MAX + 1];
1736 sys_wcstombs (tmp, NAME_MAX + 1, fname->Buffer,
1737 fname->Length / sizeof (WCHAR));
1738 fnunmunge (de->d_name, tmp);
1742 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
1743 fname->Length / sizeof (WCHAR));
1745 if (dir->__d_position == 0 && !strcmp (de->d_name, "."))
1746 dir->__flags |= dirent_saw_dot;
1747 else if (dir->__d_position == 1 && !strcmp (de->d_name, ".."))
1748 dir->__flags |= dirent_saw_dot_dot;
1753 fhandler_disk_file::readdir (DIR *dir, dirent *de)
1756 NTSTATUS status = STATUS_SUCCESS;
1757 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
1759 ULONG FileNameLength;
1760 ULONG FileAttributes = 0;
1762 UNICODE_STRING fname;
1764 /* d_cachepos always refers to the next cache entry to use. If it's 0
1765 we must reload the cache. */
1766 if (d_cachepos (dir) == 0)
1768 if ((dir->__flags & dirent_get_d_ino))
1770 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, 0, &io,
1771 d_cache (dir), DIR_BUF_SIZE,
1772 FileIdBothDirectoryInformation,
1773 FALSE, NULL, dir->__d_position == 0);
1774 /* FileIdBothDirectoryInformation isn't supported for remote drives
1775 on NT4 and 2K systems, and it's also not supported on 2K at all,
1776 when accessing network drives on any remote OS. We just fall
1777 back to using a standard directory query in this case and note
1778 this case using the dirent_get_d_ino flag. */
1779 if (status == STATUS_INVALID_LEVEL
1780 || status == STATUS_INVALID_PARAMETER
1781 || status == STATUS_INVALID_INFO_CLASS)
1782 dir->__flags &= ~dirent_get_d_ino;
1783 /* Something weird happens on Samba up to version 3.0.21c, which is
1784 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
1785 nicely, but only up to the 128th entry in the directory. After
1786 reaching this entry, the next call to NtQueryDirectoryFile
1787 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
1788 Why should we care, we can just switch to FileDirectoryInformation,
1789 isn't it? Nope! The next call to
1790 NtQueryDirectoryFile(FileDirectoryInformation)
1791 actually returns STATUS_NO_MORE_FILES, regardless how many files
1792 are left unread in the directory. This does not happen when using
1793 FileDirectoryInformation right from the start, but since
1794 we can't decide whether the server we're talking with has this
1795 bug or not, we end up serving Samba shares always in the slow
1796 mode using FileDirectoryInformation. So, what we do here is
1797 to implement the solution suggested by Andrew Tridgell, we just
1798 reread all entries up to dir->d_position using
1799 FileDirectoryInformation.
1800 However, We do *not* mark this server as broken and fall back to
1801 using FileDirectoryInformation further on. This would slow
1802 down every access to such a server, even for directories under
1803 128 entries. Also, bigger dirs only suffer from one additional
1804 call per full directory scan, which shouldn't be too big a hit.
1805 This can easily be changed if necessary. */
1806 if (status == STATUS_INVALID_LEVEL && dir->__d_position)
1808 d_cachepos (dir) = 0;
1809 for (int cnt = 0; cnt < dir->__d_position; ++cnt)
1811 if (d_cachepos (dir) == 0)
1813 status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
1814 0, &io, d_cache (dir), DIR_BUF_SIZE,
1815 FileDirectoryInformation,
1816 FALSE, NULL, cnt == 0);
1817 if (!NT_SUCCESS (status))
1820 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
1821 + d_cachepos (dir));
1822 if (buf->NextEntryOffset == 0)
1823 d_cachepos (dir) = 0;
1825 d_cachepos (dir) += buf->NextEntryOffset;
1830 if (!(dir->__flags & dirent_get_d_ino))
1831 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, 0, &io,
1832 d_cache (dir), DIR_BUF_SIZE,
1833 (dir->__flags & dirent_nfs_d_ino)
1834 ? FileNamesInformation
1835 : FileDirectoryInformation,
1836 FALSE, NULL, dir->__d_position == 0);
1841 if (!NT_SUCCESS (status))
1842 debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
1843 status, RtlNtStatusToDosError (status));
1846 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
1847 if (buf->NextEntryOffset == 0)
1848 d_cachepos (dir) = 0;
1850 d_cachepos (dir) += buf->NextEntryOffset;
1851 if ((dir->__flags & dirent_get_d_ino))
1853 FileName = buf->FileName;
1854 FileNameLength = buf->FileNameLength;
1855 FileAttributes = buf->FileAttributes;
1856 if ((dir->__flags & dirent_set_d_ino))
1857 de->d_ino = buf->FileId.QuadPart;
1859 else if ((dir->__flags & dirent_nfs_d_ino))
1861 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
1862 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
1866 FileName = ((PFILE_DIRECTORY_INFORMATION) buf)->FileName;
1867 FileNameLength = ((PFILE_DIRECTORY_INFORMATION) buf)->FileNameLength;
1868 FileAttributes = ((PFILE_DIRECTORY_INFORMATION) buf)->FileAttributes;
1870 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
1871 de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
1872 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
1874 OBJECT_ATTRIBUTES attr;
1876 if (dir->__d_position == 0 && FileNameLength == 2
1877 && FileName[0] == '.')
1878 de->d_ino = get_ino_by_handle (get_handle ());
1879 else if (dir->__d_position == 1 && FileNameLength == 4
1880 && FileName[0] == L'.' && FileName[1] == L'.')
1881 if (!(dir->__flags & dirent_isroot))
1882 de->d_ino = readdir_get_ino (get_name (), true);
1884 de->d_ino = get_ino_by_handle (get_handle ());
1889 InitializeObjectAttributes (&attr, &fname, OBJ_CASE_INSENSITIVE,
1890 get_handle (), NULL);
1891 if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
1892 FILE_SHARE_VALID_FLAGS,
1893 FILE_OPEN_FOR_BACKUP_INTENT)))
1895 de->d_ino = get_ino_by_handle (hdl);
1899 /* Enforce namehash as inode number on untrusted file systems. */
1900 if (!pc.isgood_inode (de->d_ino))
1902 dir->__flags &= ~dirent_set_d_ino;
1908 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
1909 buf ? FileAttributes : 0, &fname)))
1910 dir->__d_position++;
1911 else if (!(dir->__flags & dirent_saw_dot))
1913 strcpy (de->d_name , ".");
1914 de->d_ino = get_ino_by_handle (get_handle ());
1915 dir->__d_position++;
1916 dir->__flags |= dirent_saw_dot;
1919 else if (!(dir->__flags & dirent_saw_dot_dot))
1921 strcpy (de->d_name , "..");
1922 if (!(dir->__flags & dirent_isroot))
1923 de->d_ino = readdir_get_ino (get_name (), true);
1925 de->d_ino = get_ino_by_handle (get_handle ());
1926 dir->__d_position++;
1927 dir->__flags |= dirent_saw_dot_dot;
1931 syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, &de, res ? "***" : de->d_name);
1936 fhandler_disk_file::telldir (DIR *dir)
1938 return dir->__d_position;
1942 fhandler_disk_file::seekdir (DIR *dir, _off64_t loc)
1945 while (loc > dir->__d_position)
1946 if (!::readdir (dir))
1951 fhandler_disk_file::rewinddir (DIR *dir)
1953 d_cachepos (dir) = 0;
1954 if (wincap.has_buggy_restart_scan () && isremote ())
1956 /* This works around a W2K bug. The RestartScan parameter in calls
1957 to NtQueryDirectoryFile on remote shares is ignored, thus
1958 resulting in not being able to rewind on remote shares. By
1959 reopening the directory, we get a fresh new directory pointer. */
1960 UNICODE_STRING fname;
1961 OBJECT_ATTRIBUTES attr;
1966 RtlInitUnicodeString (&fname, L"");
1967 InitializeObjectAttributes (&attr, &fname, OBJ_CASE_INSENSITIVE,
1968 get_handle (), NULL);
1969 status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
1970 &attr, &io, FILE_SHARE_VALID_FLAGS,
1971 FILE_SYNCHRONOUS_IO_NONALERT
1972 | FILE_OPEN_FOR_BACKUP_INTENT
1973 | FILE_DIRECTORY_FILE);
1974 if (!NT_SUCCESS (stat))
1975 debug_printf ("Unable to reopen dir %s, NT error: %p",
1976 get_name (), status);
1979 NtClose (get_handle ());
1980 set_io_handle (new_dir);
1983 dir->__d_position = 0;
1984 d_mounts (dir)->rewind ();
1988 fhandler_disk_file::closedir (DIR *dir)
1993 delete d_mounts (dir);
1996 else if (get_handle () == INVALID_HANDLE_VALUE)
2001 else if (!NT_SUCCESS (status = NtClose (get_handle ())))
2003 __seterrno_from_nt_status (status);
2006 syscall_printf ("%d = closedir (%p, %s)", res, dir, get_name ());
2010 fhandler_cygdrive::fhandler_cygdrive () :
2011 fhandler_disk_file (), ndrives (0), pdrive (NULL)
2016 fhandler_cygdrive::open (int flags, mode_t mode)
2018 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2023 if (flags & O_WRONLY)
2035 fhandler_cygdrive::close ()
2040 #define DRVSZ sizeof ("x:\\")
2042 fhandler_cygdrive::set_drives ()
2044 const int len = 2 + 26 * DRVSZ;
2045 char *p = const_cast<char *> (get_win32_name ());
2047 ndrives = GetLogicalDriveStrings (len, p) / DRVSZ;
2051 fhandler_cygdrive::fstat (struct __stat64 *buf)
2053 fhandler_base::fstat (buf);
2055 buf->st_mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
2058 char flptst[] = "X:";
2060 for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
2061 if (is_floppy ((flptst[0] = *p, flptst))
2062 || GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
2064 buf->st_nlink = n + 2;
2069 fhandler_cygdrive::opendir (int fd)
2073 dir = fhandler_disk_file::opendir (fd);
2074 if (dir && !ndrives)
2081 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2083 char flptst[] = "X:";
2087 if (!pdrive || !*pdrive)
2089 if (!(dir->__flags & dirent_saw_dot))
2091 de->d_name[0] = '.';
2092 de->d_name[1] = '\0';
2097 if (!is_floppy ((flptst[0] = *pdrive, flptst))
2098 && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2100 pdrive = strchr (pdrive, '\0') + 1;
2102 *de->d_name = cyg_tolower (*pdrive);
2103 de->d_name[1] = '\0';
2104 user_shared->warned_msdos = true;
2105 de->d_ino = readdir_get_ino (pdrive, false);
2106 dir->__d_position++;
2107 pdrive = strchr (pdrive, '\0') + 1;
2108 syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2113 fhandler_cygdrive::rewinddir (DIR *dir)
2115 pdrive = get_win32_name ();
2116 dir->__d_position = 0;
2120 fhandler_cygdrive::closedir (DIR *dir)
2122 pdrive = get_win32_name ();