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 pfai;
173 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &pfai, sizeof pfai,
174 FileInternalInformation)))
175 return pfai.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);
332 /* The entries potentially contain a name of MAX_PATH wide characters. */
333 const DWORD fai_size = (NAME_MAX + 1) * sizeof (WCHAR)
334 + sizeof (FILE_ALL_INFORMATION);
335 PFILE_ALL_INFORMATION pfai = (PFILE_ALL_INFORMATION) alloca (fai_size);
337 status = NtQueryInformationFile (get_handle (), &io, pfai, fai_size,
339 if (NT_SUCCESS (status))
341 /* If the change time is 0, it's a file system which doesn't
342 support a change timestamp. In that case use the LastWriteTime
343 entry, as in other calls to fstat_helper. */
344 if (pc.is_rep_symlink ())
345 pfai->BasicInformation.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
346 pc.file_attributes (pfai->BasicInformation.FileAttributes);
347 return fstat_helper (buf,
348 pfai->BasicInformation.ChangeTime.QuadPart
349 ? *(FILETIME *) &pfai->BasicInformation.ChangeTime
350 : *(FILETIME *) &pfai->BasicInformation.LastWriteTime,
351 *(FILETIME *) &pfai->BasicInformation.LastAccessTime,
352 *(FILETIME *) &pfai->BasicInformation.LastWriteTime,
353 *(FILETIME *) &pfai->BasicInformation.CreationTime,
355 pfai->StandardInformation.EndOfFile.QuadPart,
356 pfai->StandardInformation.AllocationSize.QuadPart,
357 pfai->InternalInformation.FileId.QuadPart,
358 pfai->StandardInformation.NumberOfLinks,
359 pfai->BasicInformation.FileAttributes);
361 debug_printf ("%p = NtQueryInformationFile(%S)",
362 status, pc.get_nt_native_path ());
367 fhandler_base::fstat_by_name (struct __stat64 *buf)
370 OBJECT_ATTRIBUTES attr;
372 UNICODE_STRING dirname;
373 UNICODE_STRING basename;
375 const DWORD fdi_size = (NAME_MAX + 1) * sizeof (WCHAR)
376 + sizeof (FILE_ID_BOTH_DIR_INFORMATION);
377 const DWORD fvi_size = (NAME_MAX + 1) * sizeof (WCHAR)
378 + sizeof (FILE_FS_VOLUME_INFORMATION);
379 PFILE_ID_BOTH_DIR_INFORMATION pfdi = (PFILE_ID_BOTH_DIR_INFORMATION)
381 PFILE_FS_VOLUME_INFORMATION pfvi = (PFILE_FS_VOLUME_INFORMATION)
383 LARGE_INTEGER FileId;
387 debug_printf ("already determined that pc does not exist");
391 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
392 InitializeObjectAttributes (&attr, &dirname, OBJ_CASE_INSENSITIVE,
394 if (!NT_SUCCESS (status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
395 &attr, &io, FILE_SHARE_VALID_FLAGS,
396 FILE_SYNCHRONOUS_IO_NONALERT
397 | FILE_OPEN_FOR_BACKUP_INTENT
398 | FILE_DIRECTORY_FILE)))
400 debug_printf ("%p = NtOpenFile(%S)", status, pc.get_nt_native_path ());
403 if (wincap.has_fileid_dirinfo ()
404 && NT_SUCCESS (status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
406 FileIdBothDirectoryInformation,
407 TRUE, &basename, TRUE)))
408 FileId = pfdi->FileId;
409 else if (NT_SUCCESS (status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
411 FileBothDirectoryInformation,
412 TRUE, &basename, TRUE)))
413 FileId.QuadPart = 0; /* get_ino is called in fstat_helper. */
414 if (!NT_SUCCESS (status))
416 debug_printf ("%p = NtQueryDirectoryFile(%S)", status,
417 pc.get_nt_native_path ());
421 status = NtQueryVolumeInformationFile (dir, &io, pfvi, fvi_size,
422 FileFsVolumeInformation);
423 if (!NT_SUCCESS (status))
425 debug_printf ("%p = NtQueryVolumeInformationFile(%S)",
426 status, pc.get_nt_native_path ());
427 pfvi->VolumeSerialNumber = 0;
430 /* If the change time is 0, it's a file system which doesn't
431 support a change timestamp. In that case use the LastWriteTime
432 entry, as in other calls to fstat_helper. */
433 if (pc.is_rep_symlink ())
434 pfdi->FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
435 pc.file_attributes (pfdi->FileAttributes);
436 return fstat_helper (buf,
437 pfdi->ChangeTime.QuadPart ?
438 *(FILETIME *) &pfdi->ChangeTime :
439 *(FILETIME *) &pfdi->LastWriteTime,
440 *(FILETIME *) &pfdi->LastAccessTime,
441 *(FILETIME *) &pfdi->LastWriteTime,
442 *(FILETIME *) &pfdi->CreationTime,
443 pfvi->VolumeSerialNumber,
444 pfdi->EndOfFile.QuadPart,
445 pfdi->AllocationSize.QuadPart,
446 pfdi->FileId.QuadPart,
448 pfdi->FileAttributes);
452 /* Arbitrary value: 2006-12-01 */
453 RtlSecondsSince1970ToTime (1164931200L, &ft);
454 return fstat_helper (buf,
464 pc.file_attributes ());
468 fhandler_base::fstat_fs (struct __stat64 *buf)
472 int open_flags = O_RDONLY | O_BINARY;
476 if (!nohandle () && !is_fs_special ())
477 res = fstat_by_handle (buf);
479 res = fstat_by_name (buf);
482 query_open (query_read_attributes);
483 oret = open_fs (open_flags, 0);
486 /* We now have a valid handle, regardless of the "nohandle" state.
487 Since fhandler_base::open only calls CloseHandle if !nohandle,
488 we have to set it to false before calling close and restore
489 the state afterwards. */
490 res = fstat_by_handle (buf);
491 bool no_handle = nohandle ();
494 nohandle (no_handle);
495 set_io_handle (NULL);
498 res = fstat_by_name (buf);
503 /* The ftChangeTime is taken from the NTFS ChangeTime entry, if reading
504 the file information using NtQueryInformationFile succeeded. If not,
505 it's faked using the LastWriteTime entry from GetFileInformationByHandle
506 or FindFirstFile. We're deliberatly not using the creation time anymore
507 to simplify interaction with native Windows applications which choke on
508 creation times >= access or write times.
510 Note that the dwFileAttributes member of the file information evaluated
511 in the calling function is used here, not the pc.fileattr member, since
512 the latter might be old and not reflect the actual state of the file. */
514 fhandler_base::fstat_helper (struct __stat64 *buf,
515 FILETIME ftChangeTime,
516 FILETIME ftLastAccessTime,
517 FILETIME ftLastWriteTime,
518 FILETIME ftCreationTime,
519 DWORD dwVolumeSerialNumber,
522 ULONGLONG nFileIndex,
523 DWORD nNumberOfLinks,
524 DWORD dwFileAttributes)
527 FILE_COMPRESSION_INFORMATION fci;
529 to_timestruc_t (&ftLastAccessTime, &buf->st_atim);
530 to_timestruc_t (&ftLastWriteTime, &buf->st_mtim);
531 to_timestruc_t (&ftChangeTime, &buf->st_ctim);
532 to_timestruc_t (&ftCreationTime, &buf->st_birthtim);
533 buf->st_dev = dwVolumeSerialNumber;
534 buf->st_size = (_off64_t) nFileSize;
535 /* The number of links to a directory includes the
536 number of subdirectories in the directory, since all
537 those subdirectories point to it.
538 This is too slow on remote drives, so we do without it.
539 Setting the count to 2 confuses `find (1)' command. So
540 let's try it with `1' as link count. */
542 buf->st_nlink = pc.ndisk_links (nNumberOfLinks);
544 buf->st_nlink = nNumberOfLinks;
547 /* Enforce namehash as inode number on untrusted file systems. */
548 if (pc.isgood_inode (nFileIndex))
549 buf->st_ino = (__ino64_t) nFileIndex;
551 buf->st_ino = get_ino ();
553 buf->st_blksize = PREFERRED_IO_BLKSIZE;
555 if (nAllocSize >= 0LL)
556 /* A successful NtQueryInformationFile returns the allocation size
557 correctly for compressed and sparse files as well. */
558 buf->st_blocks = (nAllocSize + S_BLKSIZE - 1) / S_BLKSIZE;
559 else if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_COMPRESSED
560 | FILE_ATTRIBUTE_SPARSE_FILE)
561 && get_handle () && !is_fs_special ()
562 && !NtQueryInformationFile (get_handle (), &st, (PVOID) &fci,
563 sizeof fci, FileCompressionInformation))
564 /* Otherwise we request the actual amount of bytes allocated for
565 compressed and sparsed files. */
566 buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
569 /* Otherwise compute no. of blocks from file size. */
570 buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
573 /* Using a side effect: get_file_attibutes checks for
574 directory. This is used, to set S_ISVTX, if needed. */
576 buf->st_mode = S_IFDIR;
577 else if (pc.issymlink ())
579 buf->st_size = pc.get_symlink_length ();
580 /* symlinks are everything for everyone! */
581 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
582 get_file_attribute (get_handle (), pc, NULL,
583 &buf->st_uid, &buf->st_gid);
586 else if (pc.issocket ())
587 buf->st_mode = S_IFSOCK;
589 if (!get_file_attribute (is_fs_special () && !pc.issocket ()
590 ? NULL : get_handle (), pc,
591 &buf->st_mode, &buf->st_uid, &buf->st_gid))
593 /* If read-only attribute is set, modify ntsec return value */
594 if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY)
595 && !pc.isdir () && !pc.issymlink ())
596 buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
598 if (buf->st_mode & S_IFMT)
600 else if (!is_fs_special ())
601 buf->st_mode |= S_IFREG;
604 buf->st_dev = dev ();
605 buf->st_mode = dev ().mode;
611 buf->st_mode |= STD_RBITS;
613 if (!::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY))
614 buf->st_mode |= STD_WBITS;
615 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
618 buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
619 else if (buf->st_mode & S_IFMT)
621 else if (is_fs_special ())
623 buf->st_dev = dev ();
624 buf->st_mode = dev ().mode;
629 buf->st_mode |= S_IFREG;
630 if (pc.exec_state () == dont_know_if_executable)
633 OBJECT_ATTRIBUTES attr;
637 RtlInitUnicodeString (&same, L"");
638 InitializeObjectAttributes (&attr, &same, 0, get_handle (), NULL);
639 if (NT_SUCCESS (NtOpenFile (&h, FILE_READ_DATA, &attr, &io,
640 FILE_SHARE_VALID_FLAGS, 0)))
642 LARGE_INTEGER off = { QuadPart:0LL };
645 if (NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL, &io, magic,
647 && has_exec_chars (magic, io.Information))
650 buf->st_mode |= STD_XBITS;
656 if (pc.exec_state () == is_executable)
657 buf->st_mode |= STD_XBITS;
659 /* This fakes the permissions of all files to match the current umask. */
660 buf->st_mode &= ~(cygheap->umask);
664 syscall_printf ("0 = fstat (, %p) st_atime=%x st_size=%D, st_mode=%p, st_ino=%D, sizeof=%d",
665 buf, buf->st_atime, buf->st_size, buf->st_mode,
666 buf->st_ino, sizeof (*buf));
671 fhandler_disk_file::fstat (struct __stat64 *buf)
673 return fstat_fs (buf);
677 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
679 int ret = -1, opened = 0;
682 const size_t fvi_size = sizeof (FILE_FS_VOLUME_INFORMATION)
683 + (NAME_MAX + 1) * sizeof (WCHAR);
684 PFILE_FS_VOLUME_INFORMATION pfvi = (PFILE_FS_VOLUME_INFORMATION)
686 const size_t fai_size = sizeof (FILE_FS_ATTRIBUTE_INFORMATION)
687 + (NAME_MAX + 1) * sizeof (WCHAR);
688 PFILE_FS_ATTRIBUTE_INFORMATION pfai = (PFILE_FS_ATTRIBUTE_INFORMATION)
690 FILE_FS_FULL_SIZE_INFORMATION full_fsi;
691 FILE_FS_SIZE_INFORMATION fsi;
692 HANDLE fh = get_handle ();
696 OBJECT_ATTRIBUTES attr;
697 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
698 pc.get_object_attr (attr, sec_none_nih),
699 &io, FILE_SHARE_VALID_FLAGS,
700 FILE_OPEN_FOR_BACKUP_INTENT));
703 /* Can't open file. Try again with parent dir. */
704 UNICODE_STRING dirname;
705 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
706 attr.ObjectName = &dirname;
707 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
708 FILE_SHARE_VALID_FLAGS,
709 FILE_OPEN_FOR_BACKUP_INTENT));
715 /* Get basic volume information. */
716 status = NtQueryVolumeInformationFile (fh, &io, pfvi, fvi_size,
717 FileFsVolumeInformation);
718 if (!NT_SUCCESS (status))
720 __seterrno_from_nt_status (status);
723 status = NtQueryVolumeInformationFile (fh, &io, pfai, fai_size,
724 FileFsAttributeInformation);
725 if (!NT_SUCCESS (status))
727 __seterrno_from_nt_status (status);
730 sfs->f_files = ULONG_MAX;
731 sfs->f_ffree = ULONG_MAX;
732 sfs->f_favail = ULONG_MAX;
733 sfs->f_fsid = pfvi->VolumeSerialNumber;
734 sfs->f_flag = pfai->FileSystemAttributes;
735 sfs->f_namemax = pfai->MaximumComponentNameLength;
736 /* Get allocation related information. Try to get "full" information
737 first, which is only available since W2K. If that fails, try to
738 retrieve normal allocation information. */
739 status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
740 FileFsFullSizeInformation);
741 if (NT_SUCCESS (status))
743 sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
744 sfs->f_frsize = sfs->f_bsize;
745 sfs->f_blocks = full_fsi.TotalAllocationUnits.LowPart;
746 sfs->f_bfree = full_fsi.ActualAvailableAllocationUnits.LowPart;
747 sfs->f_bavail = full_fsi.CallerAvailableAllocationUnits.LowPart;
748 if (sfs->f_bfree > sfs->f_bavail)
750 /* Quotas active. We can't trust TotalAllocationUnits. */
751 NTFS_VOLUME_DATA_BUFFER nvdb;
753 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
754 FSCTL_GET_NTFS_VOLUME_DATA,
755 NULL, 0, &nvdb, sizeof nvdb);
756 if (!NT_SUCCESS (status))
757 debug_printf ("%p = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
758 status, pc.get_nt_native_path ());
760 sfs->f_blocks = nvdb.TotalClusters.QuadPart;
766 status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
767 FileFsSizeInformation);
768 if (!NT_SUCCESS (status))
770 __seterrno_from_nt_status (status);
773 sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
774 sfs->f_frsize = sfs->f_bsize;
775 sfs->f_blocks = fsi.TotalAllocationUnits.LowPart;
776 sfs->f_bfree = fsi.AvailableAllocationUnits.LowPart;
777 sfs->f_bavail = sfs->f_bfree;
783 syscall_printf ("%d = fstatvfs (%s, %p)", ret, get_name (), sfs);
788 fhandler_disk_file::fchmod (mode_t mode)
790 extern int chmod_device (path_conv& pc, mode_t mode);
796 if (pc.is_fs_special ())
797 return chmod_device (pc, mode);
801 query_open (query_write_control);
802 if (!(oret = open (O_BINARY, 0)))
804 /* Need WRITE_DAC|WRITE_OWNER to write ACLs. */
805 if (allow_ntsec && pc.has_acls ())
807 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
808 query_open (query_write_attributes);
809 if (!(oret = open (O_BINARY, 0)))
816 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
817 Only type and mode have to be set. Apparently type isn't checked
818 for consistency, so it's sufficent to set it to NF3REG all the time. */
820 FILE_FULL_EA_INFORMATION ffei;
821 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
823 ffei_buf.ffei.NextEntryOffset = 0;
824 ffei_buf.ffei.Flags = 0;
825 ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
826 ffei_buf.ffei.EaValueLength = sizeof (fattr3);
827 strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
828 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
829 + ffei_buf.ffei.EaNameLength + 1);
830 memset (nfs_attr, 0, sizeof (fattr3));
831 nfs_attr->type = NF3REG;
832 nfs_attr->mode = mode;
833 status = NtSetEaFile (get_handle (), &io,
834 &ffei_buf.ffei, sizeof ffei_buf);
835 if (!NT_SUCCESS (status))
836 __seterrno_from_nt_status (status);
842 if (allow_ntsec && pc.has_acls ())
846 if (!set_file_attribute (get_handle (), pc,
847 ILLEGAL_UID, ILLEGAL_GID, mode)
852 /* if the mode we want has any write bits set, we can't be read only. */
853 if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
854 pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
856 pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
858 pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
860 FILE_BASIC_INFORMATION fbi;
861 fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart
862 = fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
863 fbi.FileAttributes = pc.file_attributes () ?: FILE_ATTRIBUTE_NORMAL;
864 status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
865 FileBasicInformation);
866 /* Correct NTFS security attributes have higher priority */
867 if (!allow_ntsec || !pc.has_acls ())
869 if (!NT_SUCCESS (status))
870 __seterrno_from_nt_status (status);
883 fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
887 if (!pc.has_acls () || !allow_ntsec)
889 /* fake - if not supported, pretend we're like win95
890 where it just works */
891 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
897 query_open (query_write_control);
898 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
905 int res = get_file_attribute (get_handle (), pc, &attrib, NULL, NULL);
908 /* Typical Windows default ACLs can contain permissions for one
909 group, while being owned by another user/group. The permission
910 bits returned above are pretty much useless then. Creating a
911 new ACL with these useless permissions results in a potentially
912 broken symlink. So what we do here is to set the underlying
913 permissions of symlinks to a sensible value which allows the
914 world to read the symlink and only the new owner to change it. */
916 attrib = S_IFLNK | STD_RBITS | STD_WBITS;
917 res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
926 fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp)
931 if (!pc.has_acls () || !allow_ntsec)
939 /* Open for writing required to be able to set ctime
940 (even though setting the ACL is just pretended). */
942 oret = open (O_WRONLY | O_BINARY, 0);
948 else if (nentries < MIN_ACL_ENTRIES)
954 query_open (query_read_attributes);
955 oret = open (O_BINARY, 0);
957 if ((!oret && !fstat_by_handle (&st))
958 || !fstat_by_name (&st))
960 aclbufp[0].a_type = USER_OBJ;
961 aclbufp[0].a_id = st.st_uid;
962 aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
963 aclbufp[1].a_type = GROUP_OBJ;
964 aclbufp[1].a_id = st.st_gid;
965 aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
966 aclbufp[2].a_type = OTHER_OBJ;
967 aclbufp[2].a_id = ILLEGAL_GID;
968 aclbufp[2].a_perm = st.st_mode & S_IRWXO;
969 aclbufp[3].a_type = CLASS_OBJ;
970 aclbufp[3].a_id = ILLEGAL_GID;
971 aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
972 res = MIN_ACL_ENTRIES;
977 res = MIN_ACL_ENTRIES;
988 query_open (cmd == SETACL ? query_write_control : query_read_control);
989 if (!(oret = open (O_BINARY, 0)))
991 if (cmd == GETACL || cmd == GETACLCNT)
992 goto cant_access_acl;
999 if (!aclsort32 (nentries, 0, aclbufp))
1002 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1006 FILE_BASIC_INFORMATION fbi;
1007 fbi.CreationTime.QuadPart
1008 = fbi.LastAccessTime.QuadPart
1009 = fbi.LastWriteTime.QuadPart
1010 = fbi.ChangeTime.QuadPart = 0LL;
1011 fbi.FileAttributes = (pc.file_attributes ()
1012 & ~FILE_ATTRIBUTE_READONLY)
1013 ?: FILE_ATTRIBUTE_NORMAL;
1014 NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1015 FileBasicInformation);
1023 res = getacl (get_handle (), pc, nentries, aclbufp);
1026 res = getacl (get_handle (), pc, 0, NULL);
1041 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1043 if (pc.is_fs_special ())
1045 set_errno (ENOTSUP);
1048 return read_ea (get_handle (), pc, name, (char *) value, size);
1052 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1055 if (pc.is_fs_special ())
1057 set_errno (ENOTSUP);
1060 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1064 fhandler_disk_file::fadvise (_off64_t offset, _off64_t length, int advice)
1066 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1072 /* Windows only supports advice flags for the whole file. We're using
1073 a simplified test here so that we don't have to ask for the actual
1074 file size. Length == 0 means all bytes starting at offset anyway.
1075 So we only actually follow the advice, if it's given for offset == 0. */
1079 /* We only support normal and sequential mode for now. Everything which
1080 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1081 if (advice != POSIX_FADV_SEQUENTIAL)
1082 advice = POSIX_FADV_NORMAL;
1085 FILE_MODE_INFORMATION fmi;
1086 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1088 FileModeInformation);
1089 if (!NT_SUCCESS (status))
1090 __seterrno_from_nt_status (status);
1093 fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1094 if (advice == POSIX_FADV_SEQUENTIAL)
1095 fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1096 status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1097 FileModeInformation);
1098 if (NT_SUCCESS (status))
1100 __seterrno_from_nt_status (status);
1107 fhandler_disk_file::ftruncate (_off64_t length, bool allow_truncate)
1111 if (length < 0 || !get_handle ())
1113 else if (pc.isdir ())
1115 else if (!(get_access () & GENERIC_WRITE))
1121 FILE_STANDARD_INFORMATION fsi;
1122 FILE_END_OF_FILE_INFORMATION feofi;
1124 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1125 FileStandardInformation);
1126 if (!NT_SUCCESS (status))
1128 __seterrno_from_nt_status (status);
1132 /* If called through posix_fallocate, silently succeed if length
1133 is less than the file's actual length. */
1134 if (!allow_truncate && length < fsi.EndOfFile.QuadPart)
1137 feofi.EndOfFile.QuadPart = length;
1138 /* Create sparse files only when called through ftruncate, not when
1139 called through posix_fallocate. */
1141 && (pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES)
1142 && length >= fsi.EndOfFile.QuadPart + (128 * 1024))
1144 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1145 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1146 syscall_printf ("%p = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1147 status, pc.get_nt_native_path ());
1149 status = NtSetInformationFile (get_handle (), &io,
1150 &feofi, sizeof feofi,
1151 FileEndOfFileInformation);
1152 if (!NT_SUCCESS (status))
1153 __seterrno_from_nt_status (status);
1161 fhandler_disk_file::link (const char *newpath)
1163 extern bool allow_winsymlinks;
1165 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
1168 set_errno (newpc.case_clash ? ECASECLASH : newpc.error);
1172 if (newpc.exists ())
1174 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1179 char new_buf[strlen (newpath) + 5];
1180 if (!newpc.error && !newpc.case_clash)
1182 if (allow_winsymlinks && pc.is_lnk_special ())
1184 /* Shortcut hack. */
1185 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1187 newpc.check (newpath, PC_SYM_NOFOLLOW);
1189 else if (!pc.isdir ()
1191 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1194 /* Executable hack. */
1195 stpcpy (stpcpy (new_buf, newpath), ".exe");
1197 newpc.check (newpath, PC_SYM_NOFOLLOW);
1203 OBJECT_ATTRIBUTES attr;
1205 status = NtOpenFile (&fh, READ_CONTROL,
1206 pc.get_object_attr (attr, sec_none_nih), &io,
1207 FILE_SHARE_VALID_FLAGS,
1208 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
1209 if (!NT_SUCCESS (status))
1211 __seterrno_from_nt_status (status);
1214 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1215 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1216 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1217 pfli->ReplaceIfExists = FALSE;
1218 pfli->RootDirectory = NULL;
1219 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1220 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1222 if (!NT_SUCCESS (status))
1224 if (status == STATUS_INVALID_DEVICE_REQUEST)
1226 /* FS doesn't support hard links. Try to copy file. */
1227 WCHAR pcw[pc.get_nt_native_path ()->Length + 1];
1228 WCHAR newpcw[newpc.get_nt_native_path ()->Length + 1];
1229 if (!CopyFileW (pc.get_wide_win32_path (pcw),
1230 newpc.get_wide_win32_path (newpcw), TRUE))
1235 if (!allow_winsymlinks && pc.is_lnk_special ())
1236 SetFileAttributesW (newpcw, pc.file_attributes ()
1237 | FILE_ATTRIBUTE_SYSTEM
1238 | FILE_ATTRIBUTE_READONLY);
1242 __seterrno_from_nt_status (status);
1250 fhandler_disk_file::utimens (const struct timespec *tvp)
1252 return utimens_fs (tvp);
1256 fhandler_base::utimens_fs (const struct timespec *tvp)
1258 LARGE_INTEGER lastaccess, lastwrite;
1259 struct timespec timeofday;
1260 struct timespec tmp[2];
1261 bool closeit = false;
1265 query_open (query_write_attributes);
1266 if (!open_fs (O_BINARY, 0))
1268 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1269 to change the timestamps. Unfortunately it's not sufficient for a
1270 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1271 for writing, though this fails for R/O files of course. */
1272 query_open (no_query);
1273 if (!open_fs (O_WRONLY | O_BINARY, 0))
1275 syscall_printf ("Opening file failed");
1282 gettimeofday (reinterpret_cast<struct timeval *> (&timeofday), 0);
1283 timeofday.tv_nsec *= 1000;
1285 tmp[1] = tmp[0] = timeofday;
1288 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1289 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1294 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1295 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1297 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1298 timespec_to_filetime (&tmp[0], (FILETIME *) &lastaccess);
1299 timespec_to_filetime (&tmp[1], (FILETIME *) &lastwrite);
1300 debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
1303 FILE_BASIC_INFORMATION fbi;
1304 fbi.CreationTime.QuadPart = 0LL;
1305 fbi.LastAccessTime = lastaccess;
1306 fbi.LastWriteTime = lastwrite;
1307 fbi.ChangeTime.QuadPart = 0LL;
1308 fbi.FileAttributes = 0;
1309 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1310 FileBasicInformation);
1313 /* Opening a directory on a 9x share from a NT machine works(!), but
1314 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1315 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1317 __seterrno_from_nt_status (status);
1323 fhandler_disk_file::fhandler_disk_file () :
1328 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1335 fhandler_disk_file::open (int flags, mode_t mode)
1337 return open_fs (flags, mode);
1341 fhandler_base::open_fs (int flags, mode_t mode)
1343 if (pc.case_clash && flags & O_CREAT)
1345 debug_printf ("case clash detected");
1346 set_errno (ECASECLASH);
1350 /* Unfortunately NT allows to open directories for writing, but that's
1351 disallowed according to SUSv3. */
1352 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1358 int res = fhandler_base::open (flags | O_DIROPEN, mode);
1362 /* This is for file systems known for having a buggy CreateFile call
1363 which might return a valid HANDLE without having actually opened
1365 The only known file system to date is the SUN NFS Solstice Client 3.1
1366 which returns a valid handle when trying to open a file in a nonexistent
1368 if (pc.has_buggy_open () && !pc.exists ())
1370 debug_printf ("Buggy open detected.");
1376 if (pc.hasgood_inode ())
1377 ino = get_ino_by_handle (get_handle ());
1378 /* A unique ID is necessary to recognize fhandler entries which are
1379 duplicated by dup(2) or fork(2). */
1380 AllocateLocallyUniqueId ((PLUID) &unique_id);
1383 syscall_printf ("%d = fhandler_disk_file::open (%S, %p)", res,
1384 pc.get_nt_native_path (), flags);
1389 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
1392 _off64_t curpos = lseek (0, SEEK_CUR);
1393 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1397 size_t tmp_count = count;
1398 read (buf, tmp_count);
1399 if (lseek (curpos, SEEK_SET) >= 0)
1400 res = (ssize_t) tmp_count;
1404 debug_printf ("%d = pread (%p, %d, %d)\n", res, buf, count, offset);
1409 fhandler_disk_file::pwrite (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 res = (ssize_t) write (buf, count);
1418 if (lseek (curpos, SEEK_SET) < 0)
1421 debug_printf ("%d = pwrite (%p, %d, %d)\n", res, buf, count, offset);
1426 fhandler_disk_file::mkdir (mode_t mode)
1429 SECURITY_ATTRIBUTES sa = sec_none_nih;
1430 security_descriptor sd;
1432 if (allow_ntsec && has_acls ())
1433 set_security_attribute (S_IFDIR | ((mode & 07777) & ~cygheap->umask),
1438 OBJECT_ATTRIBUTES attr;
1440 ULONG fattr = FILE_ATTRIBUTE_DIRECTORY;
1441 PFILE_FULL_EA_INFORMATION p = NULL;
1444 if (pc.fs_is_nfs ())
1446 /* When creating a dir on an NFS share, we have to set the
1447 file mode by writing a NFS fattr3 structure with the
1448 correct mode bits set. */
1449 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1451 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1452 p->NextEntryOffset = 0;
1454 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1455 p->EaValueLength = sizeof (fattr3);
1456 strcpy (p->EaName, NFS_V3_ATTR);
1457 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1458 memset (nfs_attr, 0, sizeof (fattr3));
1459 nfs_attr->type = NF3DIR;
1460 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1462 status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
1463 pc.get_object_attr (attr, sa), &io, NULL,
1464 fattr, FILE_SHARE_VALID_FLAGS, FILE_CREATE,
1465 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1466 | FILE_OPEN_FOR_BACKUP_INTENT,
1468 if (NT_SUCCESS (status))
1474 __seterrno_from_nt_status (status);
1480 fhandler_disk_file::rmdir ()
1482 extern NTSTATUS unlink_nt (path_conv &pc);
1486 set_errno (ENOTDIR);
1495 NTSTATUS status = unlink_nt (pc);
1497 /* Check for existence of remote dirs after trying to delete them.
1499 - Sometimes SMB indicates failure when it really succeeds.
1500 - Removeing a directory on a samba drive doesn't return an error if the
1501 directory can't be removed because it's not empty. */
1504 OBJECT_ATTRIBUTES attr;
1505 FILE_BASIC_INFORMATION fbi;
1507 if (NT_SUCCESS (NtQueryAttributesFile
1508 (pc.get_object_attr (attr, sec_none_nih), &fbi)))
1509 status = STATUS_DIRECTORY_NOT_EMPTY;
1511 status = STATUS_SUCCESS;
1513 if (!NT_SUCCESS (status))
1515 __seterrno_from_nt_status (status);
1521 /* This is the minimal number of entries which fit into the readdir cache.
1522 The number of bytes allocated by the cache is determined by this number,
1523 To tune caching, just tweak this number. To get a feeling for the size,
1524 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1526 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1528 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1529 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1530 + (NAME_MAX + 1) * sizeof (WCHAR)))
1534 char __cache[DIR_BUF_SIZE]; /* W2K needs this buffer 8 byte aligned. */
1538 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1539 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1541 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1544 fhandler_disk_file::opendir (int fd)
1550 set_errno (ENOTDIR);
1551 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1553 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1559 else if ((dir->__d_dirent =
1560 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1568 if (cfd < 0 && fd < 0)
1571 dir->__d_dirent->__d_version = __DIRENT_VERSION;
1572 dir->__d_cookie = __DIRENT_COOKIE;
1573 dir->__handle = INVALID_HANDLE_VALUE;
1574 dir->__d_position = 0;
1575 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1576 ? dirent_isroot : 0;
1577 dir->__d_internal = (unsigned) new __DIR_mounts (get_name ());
1578 d_cachepos (dir) = 0;
1580 if (!pc.iscygdrive ())
1584 /* opendir() case. Initialize with given directory name and
1585 NULL directory handle. */
1586 OBJECT_ATTRIBUTES attr;
1590 status = NtOpenFile (&get_handle (),
1591 SYNCHRONIZE | FILE_LIST_DIRECTORY,
1592 pc.get_object_attr (attr, sec_none_nih),
1593 &io, FILE_SHARE_VALID_FLAGS,
1594 FILE_SYNCHRONOUS_IO_NONALERT
1595 | FILE_OPEN_FOR_BACKUP_INTENT
1596 | FILE_DIRECTORY_FILE);
1597 if (!NT_SUCCESS (status))
1599 __seterrno_from_nt_status (status);
1604 /* FileIdBothDirectoryInformation is apparently unsupported on
1605 XP when accessing directories on UDF. When trying to use it
1606 so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1607 It's not clear if the call isn't also unsupported on other
1608 OS/FS combinations (say, Win2K/CDFS or so). Instead of
1609 testing in readdir for yet another error code, let's use
1610 FileIdBothDirectoryInformation only on filesystems supporting
1611 persistent ACLs, FileBothDirectoryInformation otherwise. */
1612 if (pc.hasgood_inode ())
1614 dir->__flags |= dirent_set_d_ino;
1615 if (wincap.has_fileid_dirinfo ())
1616 dir->__flags |= dirent_get_d_ino;
1623 /* Filling cfd with `this' (aka storing this in the file
1624 descriptor table should only happen after it's clear that
1625 opendir doesn't fail, otherwise we end up cfree'ing the
1626 fhandler twice, once in opendir() in dir.cc, the second
1627 time on exit. Nasty, nasty... */
1630 if (pc.iscygdrive ())
1631 cfd->nohandle (true);
1633 set_close_on_exec (true);
1638 syscall_printf ("%p = opendir (%s)", res, get_name ());
1642 delete d_mounts (dir);
1644 free (dir->__d_dirent);
1646 free (dir->__d_dirname);
1653 readdir_get_ino (const char *path, bool dot_dot)
1658 OBJECT_ATTRIBUTES attr;
1664 fname = (char *) alloca (strlen (path) + 4);
1665 char *c = stpcpy (fname, path);
1671 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX);
1672 if (pc.isspecial ())
1674 if (!stat_worker (pc, &st))
1677 else if (!pc.hasgood_inode ())
1678 ino = hash_path_name (0, pc.get_nt_native_path ());
1679 else if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1680 pc.get_object_attr (attr, sec_none_nih),
1681 &io, FILE_SHARE_VALID_FLAGS,
1682 FILE_OPEN_FOR_BACKUP_INTENT
1683 | (pc.is_rep_symlink ()
1684 ? FILE_OPEN_REPARSE_POINT : 0))))
1686 ino = get_ino_by_handle (hdl);
1693 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1694 DWORD attr, PUNICODE_STRING fname)
1699 if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
1702 return geterrno_from_win_error (w32_err);
1705 dir->__flags &= ~dirent_set_d_ino;
1708 /* Check for directory reparse point. These are potential volume mount
1709 points which have another inode than the underlying directory. */
1710 if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1711 == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1714 OBJECT_ATTRIBUTES attr;
1717 InitializeObjectAttributes (&attr, fname, OBJ_CASE_INSENSITIVE,
1718 get_handle (), NULL);
1719 if (is_volume_mountpoint (&attr)
1720 && (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
1721 FILE_SHARE_VALID_FLAGS,
1722 FILE_OPEN_FOR_BACKUP_INTENT))))
1724 de->d_ino = get_ino_by_handle (reph);
1729 /* Check for Windows shortcut. If it's a Cygwin or U/WIN
1730 symlink, drop the .lnk suffix. */
1731 if ((attr & FILE_ATTRIBUTE_READONLY) && fname->Length > 4 * sizeof (WCHAR))
1733 UNICODE_STRING uname;
1734 UNICODE_STRING lname;
1736 RtlInitCountedUnicodeString (&uname,
1738 + fname->Length / sizeof (WCHAR) - 4,
1739 4 * sizeof (WCHAR));
1740 RtlInitUnicodeString (&lname, (PWCHAR) L".lnk");
1741 if (RtlEqualUnicodeString (&uname, &lname, TRUE))
1744 UNICODE_STRING fbuf;
1747 RtlCopyUnicodeString (&fbuf, pc.get_nt_native_path ());
1748 RtlAppendUnicodeToString (&fbuf, L"\\");
1749 RtlAppendUnicodeStringToString (&fbuf, fname);
1750 fbuf.Buffer += 4; /* Skip leading \??\ */
1751 fbuf.Length -= 4 * sizeof (WCHAR);
1752 if (fbuf.Buffer[1] != L':') /* UNC path */
1754 *(fbuf.Buffer += 2) = L'\\';
1755 fbuf.Length -= 2 * sizeof (WCHAR);
1757 path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
1758 if (fpath.issymlink () || fpath.is_fs_special ())
1759 fname->Length -= 4 * sizeof (WCHAR);
1764 if (pc.isencoded ())
1766 char tmp[NAME_MAX + 1];
1767 sys_wcstombs (tmp, NAME_MAX + 1, fname->Buffer,
1768 fname->Length / sizeof (WCHAR));
1769 fnunmunge (de->d_name, tmp);
1773 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
1774 fname->Length / sizeof (WCHAR));
1776 if (dir->__d_position == 0 && !strcmp (de->d_name, "."))
1777 dir->__flags |= dirent_saw_dot;
1778 else if (dir->__d_position == 1 && !strcmp (de->d_name, ".."))
1779 dir->__flags |= dirent_saw_dot_dot;
1784 fhandler_disk_file::readdir (DIR *dir, dirent *de)
1787 NTSTATUS status = STATUS_SUCCESS;
1788 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
1791 UNICODE_STRING fname;
1793 /* d_cachepos always refers to the next cache entry to use. If it's 0
1794 we must reload the cache. */
1795 if (d_cachepos (dir) == 0)
1797 if ((dir->__flags & dirent_get_d_ino))
1799 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, 0, &io,
1800 d_cache (dir), DIR_BUF_SIZE,
1801 FileIdBothDirectoryInformation,
1802 FALSE, NULL, dir->__d_position == 0);
1803 /* FileIdBothDirectoryInformation isn't supported for remote drives
1804 on NT4 and 2K systems, and it's also not supported on 2K at all,
1805 when accessing network drives on any remote OS. We just fall
1806 back to using a standard directory query in this case and note
1807 this case using the dirent_get_d_ino flag. */
1808 if (status == STATUS_INVALID_LEVEL
1809 || status == STATUS_INVALID_PARAMETER
1810 || status == STATUS_INVALID_INFO_CLASS)
1811 dir->__flags &= ~dirent_get_d_ino;
1812 /* Something weird happens on Samba up to version 3.0.21c, which is
1813 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
1814 nicely, but only up to the 128th entry in the directory. After
1815 reaching this entry, the next call to NtQueryDirectoryFile
1816 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
1817 Why should we care, we can just switch to
1818 FileBothDirectoryInformation, isn't it? Nope! The next call to
1819 NtQueryDirectoryFile(FileBothDirectoryInformation) actually
1820 returns STATUS_NO_MORE_FILES, regardless how many files are left
1821 unread in the directory. This does not happen when using
1822 FileBothDirectoryInformation right from the start, but since
1823 we can't decide whether the server we're talking with has this
1824 bug or not, we end up serving Samba shares always in the slow
1825 mode using FileBothDirectoryInformation. So, what we do here is
1826 to implement the solution suggested by Andrew Tridgell, we just
1827 reread all entries up to dir->d_position using
1828 FileBothDirectoryInformation.
1829 However, We do *not* mark this server as broken and fall back to
1830 using FileBothDirectoryInformation further on. This would slow
1831 down every access to such a server, even for directories under
1832 128 entries. Also, bigger dirs only suffer from one additional
1833 call per full directory scan, which shouldn't be too big a hit.
1834 This can easily be changed if necessary. */
1835 if (status == STATUS_INVALID_LEVEL && dir->__d_position)
1837 d_cachepos (dir) = 0;
1838 for (int cnt = 0; cnt < dir->__d_position; ++cnt)
1840 if (d_cachepos (dir) == 0)
1842 status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
1843 0, &io, d_cache (dir), DIR_BUF_SIZE,
1844 FileBothDirectoryInformation,
1845 FALSE, NULL, cnt == 0);
1846 if (!NT_SUCCESS (status))
1849 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
1850 + d_cachepos (dir));
1851 if (buf->NextEntryOffset == 0)
1852 d_cachepos (dir) = 0;
1854 d_cachepos (dir) += buf->NextEntryOffset;
1859 if (!(dir->__flags & dirent_get_d_ino))
1860 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, 0, &io,
1861 d_cache (dir), DIR_BUF_SIZE,
1862 FileBothDirectoryInformation,
1863 FALSE, NULL, dir->__d_position == 0);
1868 if (!NT_SUCCESS (status))
1869 debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
1870 status, RtlNtStatusToDosError (status));
1873 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
1874 if (buf->NextEntryOffset == 0)
1875 d_cachepos (dir) = 0;
1877 d_cachepos (dir) += buf->NextEntryOffset;
1878 if ((dir->__flags & dirent_get_d_ino))
1880 FileName = buf->FileName;
1881 if ((dir->__flags & dirent_set_d_ino))
1882 de->d_ino = buf->FileId.QuadPart;
1885 FileName = ((PFILE_BOTH_DIR_INFORMATION) buf)->FileName;
1886 RtlInitCountedUnicodeString (&fname, FileName, buf->FileNameLength);
1887 de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
1888 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
1890 OBJECT_ATTRIBUTES attr;
1892 if (dir->__d_position == 0 && buf->FileNameLength == 2
1893 && FileName[0] == '.')
1894 de->d_ino = get_ino_by_handle (get_handle ());
1895 else if (dir->__d_position == 1 && buf->FileNameLength == 4
1896 && FileName[0] == L'.' && FileName[1] == L'.')
1897 if (!(dir->__flags & dirent_isroot))
1898 de->d_ino = readdir_get_ino (get_name (), true);
1900 de->d_ino = get_ino_by_handle (get_handle ());
1905 InitializeObjectAttributes (&attr, &fname, OBJ_CASE_INSENSITIVE,
1906 get_handle (), NULL);
1907 if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
1908 FILE_SHARE_VALID_FLAGS,
1909 FILE_OPEN_FOR_BACKUP_INTENT)))
1911 de->d_ino = get_ino_by_handle (hdl);
1915 /* Enforce namehash as inode number on untrusted file systems. */
1916 if (!pc.isgood_inode (de->d_ino))
1918 dir->__flags &= ~dirent_set_d_ino;
1924 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
1925 buf ? buf->FileAttributes : 0, &fname)))
1926 dir->__d_position++;
1927 else if (!(dir->__flags & dirent_saw_dot))
1929 strcpy (de->d_name , ".");
1930 de->d_ino = get_ino_by_handle (get_handle ());
1931 dir->__d_position++;
1932 dir->__flags |= dirent_saw_dot;
1935 else if (!(dir->__flags & dirent_saw_dot_dot))
1937 strcpy (de->d_name , "..");
1938 if (!(dir->__flags & dirent_isroot))
1939 de->d_ino = readdir_get_ino (get_name (), true);
1941 de->d_ino = get_ino_by_handle (get_handle ());
1942 dir->__d_position++;
1943 dir->__flags |= dirent_saw_dot_dot;
1947 syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, &de, res ? "***" : de->d_name);
1952 fhandler_disk_file::telldir (DIR *dir)
1954 return dir->__d_position;
1958 fhandler_disk_file::seekdir (DIR *dir, _off64_t loc)
1961 while (loc > dir->__d_position)
1962 if (!::readdir (dir))
1967 fhandler_disk_file::rewinddir (DIR *dir)
1969 d_cachepos (dir) = 0;
1970 if (wincap.has_buggy_restart_scan () && isremote ())
1972 /* This works around a W2K bug. The RestartScan parameter in calls
1973 to NtQueryDirectoryFile on remote shares is ignored, thus
1974 resulting in not being able to rewind on remote shares. By
1975 reopening the directory, we get a fresh new directory pointer. */
1976 UNICODE_STRING fname;
1977 OBJECT_ATTRIBUTES attr;
1982 RtlInitUnicodeString (&fname, L"");
1983 InitializeObjectAttributes (&attr, &fname, OBJ_CASE_INSENSITIVE,
1984 get_handle (), NULL);
1985 status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
1986 &attr, &io, FILE_SHARE_VALID_FLAGS,
1987 FILE_SYNCHRONOUS_IO_NONALERT
1988 | FILE_OPEN_FOR_BACKUP_INTENT
1989 | FILE_DIRECTORY_FILE);
1990 if (!NT_SUCCESS (stat))
1991 debug_printf ("Unable to reopen dir %s, NT error: %p",
1992 get_name (), status);
1995 NtClose (get_handle ());
1996 set_io_handle (new_dir);
1999 dir->__d_position = 0;
2000 d_mounts (dir)->rewind ();
2004 fhandler_disk_file::closedir (DIR *dir)
2009 delete d_mounts (dir);
2012 else if (get_handle () == INVALID_HANDLE_VALUE)
2017 else if (!NT_SUCCESS (status = NtClose (get_handle ())))
2019 __seterrno_from_nt_status (status);
2022 syscall_printf ("%d = closedir (%p, %s)", res, dir, get_name ());
2026 fhandler_cygdrive::fhandler_cygdrive () :
2027 fhandler_disk_file (), ndrives (0), pdrive (NULL)
2032 fhandler_cygdrive::open (int flags, mode_t mode)
2034 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2039 if (flags & O_WRONLY)
2051 fhandler_cygdrive::close ()
2056 #define DRVSZ sizeof ("x:\\")
2058 fhandler_cygdrive::set_drives ()
2060 const int len = 2 + 26 * DRVSZ;
2061 char *p = const_cast<char *> (get_win32_name ());
2063 ndrives = GetLogicalDriveStrings (len, p) / DRVSZ;
2067 fhandler_cygdrive::fstat (struct __stat64 *buf)
2069 fhandler_base::fstat (buf);
2071 buf->st_mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
2074 char flptst[] = "X:";
2076 for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
2077 if (is_floppy ((flptst[0] = *p, flptst))
2078 || GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
2080 buf->st_nlink = n + 2;
2085 fhandler_cygdrive::opendir (int fd)
2089 dir = fhandler_disk_file::opendir (fd);
2090 if (dir && !ndrives)
2097 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2099 char flptst[] = "X:";
2103 if (!pdrive || !*pdrive)
2105 if (!(dir->__flags & dirent_saw_dot))
2107 de->d_name[0] = '.';
2108 de->d_name[1] = '\0';
2113 if (!is_floppy ((flptst[0] = *pdrive, flptst))
2114 && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2116 pdrive = strchr (pdrive, '\0') + 1;
2118 *de->d_name = cyg_tolower (*pdrive);
2119 de->d_name[1] = '\0';
2120 user_shared->warned_msdos = true;
2121 de->d_ino = readdir_get_ino (pdrive, false);
2122 dir->__d_position++;
2123 pdrive = strchr (pdrive, '\0') + 1;
2124 syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2129 fhandler_cygdrive::rewinddir (DIR *dir)
2131 pdrive = get_win32_name ();
2132 dir->__d_position = 0;
2136 fhandler_cygdrive::closedir (DIR *dir)
2138 pdrive = get_win32_name ();