1 /* fhandler_disk_file.cc
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008, 2009 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"
30 #define _COMPILING_NEWLIB
36 const char *parent_dir;
38 UNICODE_STRING mounts[MAX_MOUNTS];
39 bool found[MAX_MOUNTS + 2];
40 UNICODE_STRING cygdrive;
42 #define __DIR_PROC (MAX_MOUNTS)
43 #define __DIR_CYGDRIVE (MAX_MOUNTS+1)
45 __ino64_t eval_ino (int idx)
48 char fname[parent_dir_len + mounts[idx].Length + 2];
51 char *c = stpcpy (fname, parent_dir);
54 sys_wcstombs (c, mounts[idx].Length + 1,
55 mounts[idx].Buffer, mounts[idx].Length / sizeof (WCHAR));
56 path_conv pc (fname, PC_SYM_NOFOLLOW | PC_POSIX);
57 if (!stat_worker (pc, &st))
63 __DIR_mounts (const char *posix_path)
64 : parent_dir (posix_path)
66 parent_dir_len = strlen (parent_dir);
67 count = mount_table->get_mounts_here (parent_dir, parent_dir_len, mounts,
73 for (int i = 0; i < count; ++i)
74 RtlFreeUnicodeString (&mounts[i]);
75 RtlFreeUnicodeString (&cygdrive);
77 __ino64_t check_mount (PUNICODE_STRING fname, __ino64_t ino,
80 if (parent_dir_len == 1) /* root dir */
82 if (RtlEqualUnicodeString (fname, &ro_u_proc, FALSE))
84 found[__DIR_PROC] = true;
87 if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2
88 && RtlEqualUnicodeString (fname, &cygdrive, FALSE))
90 found[__DIR_CYGDRIVE] = true;
94 for (int i = 0; i < count; ++i)
95 if (RtlEqualUnicodeString (fname, &mounts[i], FALSE))
98 return eval ? eval_ino (i) : 1;
102 __ino64_t check_missing_mount (PUNICODE_STRING retname = NULL)
104 for (int i = 0; i < count; ++i)
110 *retname = mounts[i];
115 if (parent_dir_len == 1) /* root dir */
117 if (!found[__DIR_PROC])
119 found[__DIR_PROC] = true;
121 RtlInitUnicodeString (retname, L"proc");
124 if (!found[__DIR_CYGDRIVE])
126 found[__DIR_CYGDRIVE] = true;
127 if (cygdrive.Length > 0)
137 void rewind () { memset (found, 0, sizeof found); }
141 path_conv::isgood_inode (__ino64_t ino) const
143 /* We can't trust remote inode numbers of only 32 bit. That means,
144 all remote inode numbers when running under NT4, as well as remote NT4
145 NTFS, as well as shares of Samba version < 3.0.
146 The known exception are SFU NFS shares, which return the valid 32 bit
147 inode number from the remote file system unchanged. */
148 return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ());
152 is_volume_mountpoint (POBJECT_ATTRIBUTES attr)
158 if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, attr, &io,
159 FILE_SHARE_VALID_FLAGS,
160 FILE_OPEN_FOR_BACKUP_INTENT
161 | FILE_OPEN_REPARSE_POINT)))
163 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER)
164 alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
165 if (NT_SUCCESS (NtFsControlFile (reph, NULL, NULL, NULL,
166 &io, FSCTL_GET_REPARSE_POINT, NULL, 0,
167 (LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE))
168 && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT
169 && rp->SymbolicLinkReparseBuffer.PrintNameLength == 0)
176 static inline __ino64_t
177 get_ino_by_handle (path_conv &pc, HANDLE hdl)
180 FILE_INTERNAL_INFORMATION fai;
182 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
183 FileInternalInformation))
184 && pc.isgood_inode (fai.FileId.QuadPart))
185 return fai.FileId.QuadPart;
190 path_conv::ndisk_links (DWORD nNumberOfLinks)
192 if (!isdir () || isremote ())
193 return nNumberOfLinks;
195 OBJECT_ATTRIBUTES attr;
199 if (!NT_SUCCESS (NtOpenFile (&fh, SYNCHRONIZE | FILE_LIST_DIRECTORY,
200 get_object_attr (attr, sec_none_nih),
201 &io, FILE_SHARE_VALID_FLAGS,
202 FILE_SYNCHRONOUS_IO_NONALERT
203 | FILE_OPEN_FOR_BACKUP_INTENT
204 | FILE_DIRECTORY_FILE)))
205 return nNumberOfLinks;
209 PFILE_DIRECTORY_INFORMATION fdibuf = (PFILE_DIRECTORY_INFORMATION)
211 __DIR_mounts *dir = new __DIR_mounts (normalized_path);
212 while (NT_SUCCESS (NtQueryDirectoryFile (fh, NULL, NULL, NULL, &io, fdibuf,
213 65536, FileDirectoryInformation,
214 FALSE, NULL, first)))
219 /* All directories have . and .. as their first entries.
220 If . is not present as first entry, we're on a drive's
221 root direcotry, which doesn't have these entries. */
222 if (fdibuf->FileNameLength != 2 || fdibuf->FileName[0] != L'.')
225 for (PFILE_DIRECTORY_INFORMATION pfdi = fdibuf;
227 pfdi = (PFILE_DIRECTORY_INFORMATION)
228 (pfdi->NextEntryOffset ? (PBYTE) pfdi + pfdi->NextEntryOffset
231 switch (pfdi->FileAttributes
232 & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
234 case FILE_ATTRIBUTE_DIRECTORY:
235 /* Just a directory */
238 case FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT:
239 /* Volume mount point or symlink to directory */
241 UNICODE_STRING fname;
243 RtlInitCountedUnicodeString (&fname, pfdi->FileName,
244 pfdi->FileNameLength);
245 InitializeObjectAttributes (&attr, &fname,
246 objcaseinsensitive (), fh, NULL);
247 if (is_volume_mountpoint (&attr))
254 UNICODE_STRING fname;
255 RtlInitCountedUnicodeString (&fname, pfdi->FileName,
256 pfdi->FileNameLength);
257 dir->check_mount (&fname, 0, false);
260 while (dir->check_missing_mount ())
267 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
268 This returns the content of a struct fattr3 as defined in RFC 1813.
269 The content is the NFS equivalent of struct stat. so there's not much
270 to do here except for copying. */
272 fhandler_base::fstat_by_nfs_ea (struct __stat64 *buf)
277 FILE_FULL_EA_INFORMATION ffei;
278 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
281 FILE_GET_EA_INFORMATION fgei;
282 char buf[sizeof (NFS_V3_ATTR)];
285 fgei_buf.fgei.NextEntryOffset = 0;
286 fgei_buf.fgei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
287 stpcpy (fgei_buf.fgei.EaName, NFS_V3_ATTR);
288 status = NtQueryEaFile (get_handle (), &io,
289 &ffei_buf.ffei, sizeof ffei_buf, TRUE,
290 &fgei_buf.fgei, sizeof fgei_buf, NULL, TRUE);
291 if (NT_SUCCESS (status))
293 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
294 + ffei_buf.ffei.EaNameLength + 1);
295 buf->st_dev = nfs_attr->fsid;
296 buf->st_ino = nfs_attr->fileid;
297 buf->st_mode = (nfs_attr->mode & 0xfff)
298 | nfs_type_mapping[nfs_attr->type & 7];
299 buf->st_nlink = nfs_attr->nlink;
300 /* FIXME: How to convert UNIX uid/gid to Windows SIDs? */
302 buf->st_uid = nfs_attr->uid;
303 buf->st_gid = nfs_attr->gid;
305 buf->st_uid = myself->uid;
306 buf->st_gid = myself->gid;
308 buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
309 nfs_attr->rdev.specdata2);
310 buf->st_size = nfs_attr->size;
311 buf->st_blksize = PREFERRED_IO_BLKSIZE;
312 buf->st_blocks = nfs_attr->used / 512;
313 buf->st_atim = nfs_attr->atime;
314 buf->st_mtim = nfs_attr->mtime;
315 buf->st_ctim = nfs_attr->ctime;
318 debug_printf ("%p = NtQueryEaFile(%S)", status, pc.get_nt_native_path ());
323 fhandler_base::fstat_by_handle (struct __stat64 *buf)
329 return fstat_by_nfs_ea (buf);
331 /* Don't use FileAllInformation info class. It returns a pathname rather
332 than a filename, so it needs a really big buffer for no good reason
333 since we don't need the name anyway. So we just call the three info
334 classes necessary to get all information required by stat(2). */
335 FILE_BASIC_INFORMATION fbi;
336 FILE_STANDARD_INFORMATION fsi;
337 FILE_INTERNAL_INFORMATION fii;
339 status = NtQueryInformationFile (get_handle (), &io, &fbi, sizeof fbi,
340 FileBasicInformation);
341 if (!NT_SUCCESS (status))
343 debug_printf ("%p = NtQueryInformationFile(%S, FileBasicInformation)",
344 status, pc.get_nt_native_path ());
347 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
348 FileStandardInformation);
349 if (!NT_SUCCESS (status))
351 debug_printf ("%p = NtQueryInformationFile(%S, FileStandardInformation)",
352 status, pc.get_nt_native_path ());
355 status = NtQueryInformationFile (get_handle (), &io, &fii, sizeof fii,
356 FileInternalInformation);
357 if (!NT_SUCCESS (status))
359 debug_printf ("%p = NtQueryInformationFile(%S, FileInternalInformation)",
360 status, pc.get_nt_native_path ());
363 /* If the change time is 0, it's a file system which doesn't
364 support a change timestamp. In that case use the LastWriteTime
365 entry, as in other calls to fstat_helper. */
366 if (pc.is_rep_symlink ())
367 fbi.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
368 pc.file_attributes (fbi.FileAttributes);
369 return fstat_helper (buf,
370 fbi.ChangeTime.QuadPart
371 ? *(FILETIME *) (void *) &fbi.ChangeTime
372 : *(FILETIME *) (void *) &fbi.LastWriteTime,
373 *(FILETIME *) (void *) &fbi.LastAccessTime,
374 *(FILETIME *) (void *) &fbi.LastWriteTime,
375 *(FILETIME *) (void *) &fbi.CreationTime,
377 fsi.EndOfFile.QuadPart,
378 fsi.AllocationSize.QuadPart,
385 fhandler_base::fstat_by_name (struct __stat64 *buf)
388 OBJECT_ATTRIBUTES attr;
390 UNICODE_STRING dirname;
391 UNICODE_STRING basename;
394 FILE_ID_BOTH_DIR_INFORMATION fdi;
395 WCHAR buf[NAME_MAX + 1];
397 LARGE_INTEGER FileId;
399 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
400 InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
402 if (!NT_SUCCESS (status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
403 &attr, &io, FILE_SHARE_VALID_FLAGS,
404 FILE_SYNCHRONOUS_IO_NONALERT
405 | FILE_OPEN_FOR_BACKUP_INTENT
406 | FILE_DIRECTORY_FILE)))
408 debug_printf ("%p = NtOpenFile(%S)", status, pc.get_nt_native_path ());
411 if (wincap.has_fileid_dirinfo () && !pc.has_buggy_fileid_dirinfo ()
412 && NT_SUCCESS (status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
413 &fdi_buf.fdi, sizeof fdi_buf,
414 FileIdBothDirectoryInformation,
415 TRUE, &basename, TRUE)))
416 FileId = fdi_buf.fdi.FileId;
417 else if (NT_SUCCESS (status = NtQueryDirectoryFile (dir, NULL, NULL, NULL,
420 FileDirectoryInformation,
421 TRUE, &basename, TRUE)))
422 FileId.QuadPart = 0; /* get_ino is called in fstat_helper. */
423 if (!NT_SUCCESS (status))
425 debug_printf ("%p = NtQueryDirectoryFile(%S)", status,
426 pc.get_nt_native_path ());
431 /* If the change time is 0, it's a file system which doesn't
432 support a change timestamp. In that case use the LastWriteTime
433 entry, as in other calls to fstat_helper. */
434 if (pc.is_rep_symlink ())
435 fdi_buf.fdi.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
436 pc.file_attributes (fdi_buf.fdi.FileAttributes);
437 return fstat_helper (buf,
438 fdi_buf.fdi.ChangeTime.QuadPart ?
439 *(FILETIME *) (void *) &fdi_buf.fdi.ChangeTime :
440 *(FILETIME *) (void *) &fdi_buf.fdi.LastWriteTime,
441 *(FILETIME *) (void *) &fdi_buf.fdi.LastAccessTime,
442 *(FILETIME *) (void *) &fdi_buf.fdi.LastWriteTime,
443 *(FILETIME *) (void *) &fdi_buf.fdi.CreationTime,
444 pc.fs_serial_number (),
445 fdi_buf.fdi.EndOfFile.QuadPart,
446 fdi_buf.fdi.AllocationSize.QuadPart,
449 fdi_buf.fdi.FileAttributes);
453 /* Arbitrary value: 2006-12-01 */
454 RtlSecondsSince1970ToTime (1164931200L, &ft);
455 return fstat_helper (buf,
456 *(FILETIME *) (void *) &ft,
457 *(FILETIME *) (void *) &ft,
458 *(FILETIME *) (void *) &ft,
459 *(FILETIME *) (void *) &ft,
465 pc.file_attributes ());
469 fhandler_base::fstat_fs (struct __stat64 *buf)
473 int open_flags = O_RDONLY | O_BINARY;
477 if (!nohandle () && !is_fs_special ())
478 res = fstat_by_handle (buf);
480 res = fstat_by_name (buf);
483 query_open (query_read_attributes);
484 oret = open_fs (open_flags, 0);
487 /* We now have a valid handle, regardless of the "nohandle" state.
488 Since fhandler_base::open only calls CloseHandle if !nohandle,
489 we have to set it to false before calling close and restore
490 the state afterwards. */
491 res = fstat_by_handle (buf);
492 bool no_handle = nohandle ();
495 nohandle (no_handle);
496 set_io_handle (NULL);
499 res = fstat_by_name (buf);
504 /* The ftChangeTime is taken from the NTFS ChangeTime entry, if reading
505 the file information using NtQueryInformationFile succeeded. If not,
506 it's faked using the LastWriteTime entry from GetFileInformationByHandle
507 or FindFirstFile. We're deliberatly not using the creation time anymore
508 to simplify interaction with native Windows applications which choke on
509 creation times >= access or write times.
511 Note that the dwFileAttributes member of the file information evaluated
512 in the calling function is used here, not the pc.fileattr member, since
513 the latter might be old and not reflect the actual state of the file. */
515 fhandler_base::fstat_helper (struct __stat64 *buf,
516 FILETIME ftChangeTime,
517 FILETIME ftLastAccessTime,
518 FILETIME ftLastWriteTime,
519 FILETIME ftCreationTime,
520 DWORD dwVolumeSerialNumber,
523 ULONGLONG nFileIndex,
524 DWORD nNumberOfLinks,
525 DWORD dwFileAttributes)
528 FILE_COMPRESSION_INFORMATION fci;
530 to_timestruc_t (&ftLastAccessTime, &buf->st_atim);
531 to_timestruc_t (&ftLastWriteTime, &buf->st_mtim);
532 to_timestruc_t (&ftChangeTime, &buf->st_ctim);
533 to_timestruc_t (&ftCreationTime, &buf->st_birthtim);
534 buf->st_dev = dwVolumeSerialNumber;
535 buf->st_size = (_off64_t) nFileSize;
536 /* The number of links to a directory includes the
537 number of subdirectories in the directory, since all
538 those subdirectories point to it.
539 This is too slow on remote drives, so we do without it.
540 Setting the count to 2 confuses `find (1)' command. So
541 let's try it with `1' as link count. */
543 buf->st_nlink = pc.ndisk_links (nNumberOfLinks);
545 buf->st_nlink = nNumberOfLinks;
548 /* Enforce namehash as inode number on untrusted file systems. */
549 if (pc.isgood_inode (nFileIndex))
550 buf->st_ino = (__ino64_t) nFileIndex;
552 buf->st_ino = get_ino ();
554 buf->st_blksize = PREFERRED_IO_BLKSIZE;
556 if (nAllocSize >= 0LL)
557 /* A successful NtQueryInformationFile returns the allocation size
558 correctly for compressed and sparse files as well. */
559 buf->st_blocks = (nAllocSize + S_BLKSIZE - 1) / S_BLKSIZE;
560 else if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_COMPRESSED
561 | FILE_ATTRIBUTE_SPARSE_FILE)
562 && get_handle () && !is_fs_special ()
563 && !NtQueryInformationFile (get_handle (), &st, (PVOID) &fci,
564 sizeof fci, FileCompressionInformation))
565 /* Otherwise we request the actual amount of bytes allocated for
566 compressed and sparsed files. */
567 buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
570 /* Otherwise compute no. of blocks from file size. */
571 buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
574 /* Using a side effect: get_file_attibutes checks for
575 directory. This is used, to set S_ISVTX, if needed. */
577 buf->st_mode = S_IFDIR;
578 else if (pc.issymlink ())
580 buf->st_size = pc.get_symlink_length ();
581 /* symlinks are everything for everyone! */
582 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
583 get_file_attribute (get_handle (), pc, NULL,
584 &buf->st_uid, &buf->st_gid);
587 else if (pc.issocket ())
588 buf->st_mode = S_IFSOCK;
590 if (!get_file_attribute (is_fs_special () && !pc.issocket ()
591 ? NULL : get_handle (), pc,
592 &buf->st_mode, &buf->st_uid, &buf->st_gid))
594 /* If read-only attribute is set, modify ntsec return value */
595 if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY)
596 && !pc.isdir () && !pc.issymlink ())
597 buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
599 if (buf->st_mode & S_IFMT)
601 else if (!is_fs_special ())
602 buf->st_mode |= S_IFREG;
605 buf->st_dev = dev ();
606 buf->st_mode = dev ().mode;
612 buf->st_mode |= STD_RBITS;
614 if (!::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY))
615 buf->st_mode |= STD_WBITS;
616 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
619 buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
620 else if (buf->st_mode & S_IFMT)
622 else if (is_fs_special ())
624 buf->st_dev = dev ();
625 buf->st_mode = dev ().mode;
630 buf->st_mode |= S_IFREG;
631 /* Check suffix for executable file. */
632 if (pc.exec_state () == dont_know_if_executable)
634 PUNICODE_STRING path = pc.get_nt_native_path ();
636 if (RtlEqualUnicodePathSuffix (path, &ro_u_exe, TRUE)
637 || RtlEqualUnicodePathSuffix (path, &ro_u_lnk, TRUE)
638 || RtlEqualUnicodePathSuffix (path, &ro_u_com, TRUE))
641 /* No known sufix, check file header. This catches binaries and
643 if (pc.exec_state () == dont_know_if_executable)
645 OBJECT_ATTRIBUTES attr;
649 InitializeObjectAttributes (&attr, &ro_u_empty, 0, get_handle (),
651 if (NT_SUCCESS (NtOpenFile (&h, FILE_READ_DATA, &attr, &io,
652 FILE_SHARE_VALID_FLAGS, 0)))
654 LARGE_INTEGER off = { QuadPart:0LL };
657 if (NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL, &io, magic,
659 && has_exec_chars (magic, io.Information))
662 buf->st_mode |= STD_XBITS;
668 if (pc.exec_state () == is_executable)
669 buf->st_mode |= STD_XBITS;
671 /* This fakes the permissions of all files to match the current umask. */
672 buf->st_mode &= ~(cygheap->umask);
673 /* If the FS supports ACLs, we're here because we couldn't even open
674 the file for READ_CONTROL access. Chances are high that the file's
675 security descriptor has no ACE for "Everyone", so we should not fake
676 any access for "others". */
678 buf->st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH);
682 syscall_printf ("0 = fstat (, %p) st_atime=%x st_size=%D, st_mode=%p, st_ino=%D, sizeof=%d",
683 buf, buf->st_atime, buf->st_size, buf->st_mode,
684 buf->st_ino, sizeof (*buf));
689 fhandler_disk_file::fstat (struct __stat64 *buf)
691 return fstat_fs (buf);
695 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
697 int ret = -1, opened = 0;
700 FILE_FS_FULL_SIZE_INFORMATION full_fsi;
701 FILE_FS_SIZE_INFORMATION fsi;
702 HANDLE fh = get_handle ();
706 OBJECT_ATTRIBUTES attr;
707 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
708 pc.get_object_attr (attr, sec_none_nih),
709 &io, FILE_SHARE_VALID_FLAGS,
710 FILE_OPEN_FOR_BACKUP_INTENT));
713 /* Can't open file. Try again with parent dir. */
714 UNICODE_STRING dirname;
715 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
716 attr.ObjectName = &dirname;
717 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
718 FILE_SHARE_VALID_FLAGS,
719 FILE_OPEN_FOR_BACKUP_INTENT));
725 sfs->f_files = ULONG_MAX;
726 sfs->f_ffree = ULONG_MAX;
727 sfs->f_favail = ULONG_MAX;
728 sfs->f_fsid = pc.fs_serial_number ();
729 sfs->f_flag = pc.fs_flags ();
730 sfs->f_namemax = pc.fs_name_len ();
731 /* Get allocation related information. Try to get "full" information
732 first, which is only available since W2K. If that fails, try to
733 retrieve normal allocation information. */
734 status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
735 FileFsFullSizeInformation);
736 if (NT_SUCCESS (status))
738 sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
739 sfs->f_frsize = sfs->f_bsize;
740 sfs->f_blocks = full_fsi.TotalAllocationUnits.LowPart;
741 sfs->f_bfree = full_fsi.ActualAvailableAllocationUnits.LowPart;
742 sfs->f_bavail = full_fsi.CallerAvailableAllocationUnits.LowPart;
743 if (sfs->f_bfree > sfs->f_bavail)
745 /* Quotas active. We can't trust TotalAllocationUnits. */
746 NTFS_VOLUME_DATA_BUFFER nvdb;
748 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
749 FSCTL_GET_NTFS_VOLUME_DATA,
750 NULL, 0, &nvdb, sizeof nvdb);
751 if (!NT_SUCCESS (status))
752 debug_printf ("%p = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
753 status, pc.get_nt_native_path ());
755 sfs->f_blocks = nvdb.TotalClusters.QuadPart;
761 status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
762 FileFsSizeInformation);
763 if (!NT_SUCCESS (status))
765 __seterrno_from_nt_status (status);
768 sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
769 sfs->f_frsize = sfs->f_bsize;
770 sfs->f_blocks = fsi.TotalAllocationUnits.LowPart;
771 sfs->f_bfree = fsi.AvailableAllocationUnits.LowPart;
772 sfs->f_bavail = sfs->f_bfree;
778 syscall_printf ("%d = fstatvfs (%s, %p)", ret, get_name (), sfs);
783 fhandler_disk_file::fchmod (mode_t mode)
785 extern int chmod_device (path_conv& pc, mode_t mode);
791 if (pc.is_fs_special ())
792 return chmod_device (pc, mode);
796 query_open (query_write_control);
797 if (!(oret = open (O_BINARY, 0)))
799 /* Need WRITE_DAC|WRITE_OWNER to write ACLs. */
802 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
803 query_open (query_write_attributes);
804 if (!(oret = open (O_BINARY, 0)))
811 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
812 Only type and mode have to be set. Apparently type isn't checked
813 for consistency, so it's sufficent to set it to NF3REG all the time. */
815 FILE_FULL_EA_INFORMATION ffei;
816 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
818 ffei_buf.ffei.NextEntryOffset = 0;
819 ffei_buf.ffei.Flags = 0;
820 ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
821 ffei_buf.ffei.EaValueLength = sizeof (fattr3);
822 strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
823 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
824 + ffei_buf.ffei.EaNameLength + 1);
825 memset (nfs_attr, 0, sizeof (fattr3));
826 nfs_attr->type = NF3REG;
827 nfs_attr->mode = mode;
828 status = NtSetEaFile (get_handle (), &io,
829 &ffei_buf.ffei, sizeof ffei_buf);
830 if (!NT_SUCCESS (status))
831 __seterrno_from_nt_status (status);
841 if (!set_file_attribute (get_handle (), pc,
842 ILLEGAL_UID, ILLEGAL_GID, mode))
846 /* If the mode has any write bits set, the DOS R/O flag is in the way. */
847 if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
848 pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
849 else if (!pc.has_acls ()) /* Never set DOS R/O if security is used. */
850 pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
852 pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
854 status = NtSetAttributesFile (get_handle (), pc.file_attributes ());
855 /* MVFS needs a good amount of kicking to be convinced that it has to write
856 back metadata changes and to invalidate the cached metadata information
857 stored for the given handle. This method to open a second handle to
858 the file and write the same metadata information twice has been found
859 experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
860 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !oret)
862 OBJECT_ATTRIBUTES attr;
865 InitializeObjectAttributes (&attr, &ro_u_empty, 0, get_handle (), NULL);
866 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
867 FILE_SHARE_VALID_FLAGS,
868 FILE_OPEN_FOR_BACKUP_INTENT)))
870 NtSetAttributesFile (fh, pc.file_attributes ());
874 /* Correct NTFS security attributes have higher priority */
877 if (!NT_SUCCESS (status))
878 __seterrno_from_nt_status (status);
891 fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
897 /* fake - if not supported, pretend we're like win95
898 where it just works */
899 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
905 query_open (query_write_control);
906 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
914 int res = get_file_attribute (get_handle (), pc, &attrib, &old_uid, NULL);
917 /* Typical Windows default ACLs can contain permissions for one
918 group, while being owned by another user/group. The permission
919 bits returned above are pretty much useless then. Creating a
920 new ACL with these useless permissions results in a potentially
921 broken symlink. So what we do here is to set the underlying
922 permissions of symlinks to a sensible value which allows the
923 world to read the symlink and only the new owner to change it. */
925 attrib = S_IFLNK | STD_RBITS | STD_WBITS;
926 res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
927 /* If you're running a Samba server which has no winbidd running, the
928 uid<->SID mapping is disfunctional. Even trying to chown to your
929 own account fails since the account used on the server is the UNIX
930 account which gets used for the standard user mapping. This is a
931 default mechanism which doesn't know your real Windows SID.
932 There are two possible error codes in different Samba releases for
933 this situation, one of them is unfortunately the not very significant
934 STATUS_ACCESS_DENIED. Instead of relying on the error codes, we're
935 using the below very simple heuristic. If set_file_attribute failed,
936 and the original user account was either already unknown, or one of
937 the standard UNIX accounts, we're faking success. */
938 if (res == -1 && pc.fs_is_samba ())
942 if (old_uid == ILLEGAL_UID
943 || (sid.getfrompw (internal_getpwuid (old_uid))
944 && EqualPrefixSid (sid, well_known_samba_unix_user_fake_sid)))
946 debug_printf ("Faking chown worked on standalone Samba");
958 fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp)
971 /* Open for writing required to be able to set ctime
972 (even though setting the ACL is just pretended). */
974 oret = open (O_WRONLY | O_BINARY, 0);
980 else if (nentries < MIN_ACL_ENTRIES)
986 query_open (query_read_attributes);
987 oret = open (O_BINARY, 0);
989 if ((oret && !fstat_by_handle (&st))
990 || !fstat_by_name (&st))
992 aclbufp[0].a_type = USER_OBJ;
993 aclbufp[0].a_id = st.st_uid;
994 aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
995 aclbufp[1].a_type = GROUP_OBJ;
996 aclbufp[1].a_id = st.st_gid;
997 aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
998 aclbufp[2].a_type = OTHER_OBJ;
999 aclbufp[2].a_id = ILLEGAL_GID;
1000 aclbufp[2].a_perm = st.st_mode & S_IRWXO;
1001 aclbufp[3].a_type = CLASS_OBJ;
1002 aclbufp[3].a_id = ILLEGAL_GID;
1003 aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
1004 res = MIN_ACL_ENTRIES;
1009 res = MIN_ACL_ENTRIES;
1020 query_open (cmd == SETACL ? query_write_control : query_read_control);
1021 if (!(oret = open (O_BINARY, 0)))
1023 if (cmd == GETACL || cmd == GETACLCNT)
1024 goto cant_access_acl;
1031 if (!aclsort32 (nentries, 0, aclbufp))
1034 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1038 FILE_BASIC_INFORMATION fbi;
1039 fbi.CreationTime.QuadPart
1040 = fbi.LastAccessTime.QuadPart
1041 = fbi.LastWriteTime.QuadPart
1042 = fbi.ChangeTime.QuadPart = 0LL;
1043 fbi.FileAttributes = (pc.file_attributes ()
1044 & ~FILE_ATTRIBUTE_READONLY)
1045 ?: FILE_ATTRIBUTE_NORMAL;
1046 NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1047 FileBasicInformation);
1055 res = getacl (get_handle (), pc, nentries, aclbufp);
1058 res = getacl (get_handle (), pc, 0, NULL);
1073 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1075 if (pc.is_fs_special ())
1077 set_errno (ENOTSUP);
1080 return read_ea (get_handle (), pc, name, (char *) value, size);
1084 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1087 if (pc.is_fs_special ())
1089 set_errno (ENOTSUP);
1092 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1096 fhandler_disk_file::fadvise (_off64_t offset, _off64_t length, int advice)
1098 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1104 /* Windows only supports advice flags for the whole file. We're using
1105 a simplified test here so that we don't have to ask for the actual
1106 file size. Length == 0 means all bytes starting at offset anyway.
1107 So we only actually follow the advice, if it's given for offset == 0. */
1111 /* We only support normal and sequential mode for now. Everything which
1112 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1113 if (advice != POSIX_FADV_SEQUENTIAL)
1114 advice = POSIX_FADV_NORMAL;
1117 FILE_MODE_INFORMATION fmi;
1118 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1120 FileModeInformation);
1121 if (!NT_SUCCESS (status))
1122 __seterrno_from_nt_status (status);
1125 fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1126 if (advice == POSIX_FADV_SEQUENTIAL)
1127 fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1128 status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1129 FileModeInformation);
1130 if (NT_SUCCESS (status))
1132 __seterrno_from_nt_status (status);
1139 fhandler_disk_file::ftruncate (_off64_t length, bool allow_truncate)
1143 if (length < 0 || !get_handle ())
1145 else if (pc.isdir ())
1147 else if (!(get_access () & GENERIC_WRITE))
1153 FILE_STANDARD_INFORMATION fsi;
1154 FILE_END_OF_FILE_INFORMATION feofi;
1156 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1157 FileStandardInformation);
1158 if (!NT_SUCCESS (status))
1160 __seterrno_from_nt_status (status);
1164 /* If called through posix_fallocate, silently succeed if length
1165 is less than the file's actual length. */
1166 if (!allow_truncate && length < fsi.EndOfFile.QuadPart)
1169 feofi.EndOfFile.QuadPart = length;
1170 /* Create sparse files only when called through ftruncate, not when
1171 called through posix_fallocate. */
1173 && (pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES)
1174 && length >= fsi.EndOfFile.QuadPart + (128 * 1024))
1176 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1177 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1178 syscall_printf ("%p = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1179 status, pc.get_nt_native_path ());
1181 status = NtSetInformationFile (get_handle (), &io,
1182 &feofi, sizeof feofi,
1183 FileEndOfFileInformation);
1184 if (!NT_SUCCESS (status))
1185 __seterrno_from_nt_status (status);
1193 fhandler_disk_file::link (const char *newpath)
1195 size_t nlen = strlen (newpath);
1196 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
1199 set_errno (newpc.error);
1203 if (newpc.exists ())
1205 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1210 if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
1216 char new_buf[nlen + 5];
1219 /* If the original file is a lnk special file (except for sockets),
1220 and if the original file has a .lnk suffix, add one to the hardlink
1222 if (pc.is_lnk_special () && !pc.issocket ()
1223 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1226 /* Shortcut hack. */
1227 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1229 newpc.check (newpath, PC_SYM_NOFOLLOW);
1231 else if (!pc.isdir ()
1233 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1235 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1238 /* Executable hack. */
1239 stpcpy (stpcpy (new_buf, newpath), ".exe");
1241 newpc.check (newpath, PC_SYM_NOFOLLOW);
1247 OBJECT_ATTRIBUTES attr;
1249 status = NtOpenFile (&fh, READ_CONTROL,
1250 pc.get_object_attr (attr, sec_none_nih), &io,
1251 FILE_SHARE_VALID_FLAGS,
1252 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
1253 if (!NT_SUCCESS (status))
1255 __seterrno_from_nt_status (status);
1258 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1259 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1260 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1261 pfli->ReplaceIfExists = FALSE;
1262 pfli->RootDirectory = NULL;
1263 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1264 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1266 if (!NT_SUCCESS (status))
1268 if (status == STATUS_INVALID_DEVICE_REQUEST)
1270 /* FS doesn't support hard links. Linux returns EPERM. */
1276 __seterrno_from_nt_status (status);
1284 fhandler_disk_file::utimens (const struct timespec *tvp)
1286 return utimens_fs (tvp);
1290 fhandler_base::utimens_fs (const struct timespec *tvp)
1292 struct timespec timeofday;
1293 struct timespec tmp[2];
1294 bool closeit = false;
1298 query_open (query_write_attributes);
1299 if (!open_fs (O_BINARY, 0))
1301 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1302 to change the timestamps. Unfortunately it's not sufficient for a
1303 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1304 for writing, though this fails for R/O files of course. */
1305 query_open (no_query);
1306 if (!open_fs (O_WRONLY | O_BINARY, 0))
1308 syscall_printf ("Opening file failed");
1315 clock_gettime (CLOCK_REALTIME, &timeofday);
1317 tmp[1] = tmp[0] = timeofday;
1320 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1321 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1328 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1329 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1331 debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
1334 FILE_BASIC_INFORMATION fbi;
1336 fbi.CreationTime.QuadPart = 0LL;
1337 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1338 timespec_to_filetime (&tmp[0], (LPFILETIME) &fbi.LastAccessTime);
1339 timespec_to_filetime (&tmp[1], (LPFILETIME) &fbi.LastWriteTime);
1340 fbi.ChangeTime.QuadPart = 0LL;
1341 fbi.FileAttributes = 0;
1342 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1343 FileBasicInformation);
1344 /* For this special case for MVFS see the comment in
1345 fhandler_disk_file::fchmod. */
1346 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
1348 OBJECT_ATTRIBUTES attr;
1351 InitializeObjectAttributes (&attr, &ro_u_empty, 0, get_handle (), NULL);
1352 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
1353 FILE_SHARE_VALID_FLAGS,
1354 FILE_OPEN_FOR_BACKUP_INTENT)))
1356 NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1357 FileBasicInformation);
1363 /* Opening a directory on a 9x share from a NT machine works(!), but
1364 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1365 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1367 __seterrno_from_nt_status (status);
1373 fhandler_disk_file::fhandler_disk_file () :
1378 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1385 fhandler_disk_file::open (int flags, mode_t mode)
1387 return open_fs (flags, mode);
1391 fhandler_base::open_fs (int flags, mode_t mode)
1393 /* Unfortunately NT allows to open directories for writing, but that's
1394 disallowed according to SUSv3. */
1395 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1401 int res = fhandler_base::open (flags | O_DIROPEN, mode);
1405 /* This is for file systems known for having a buggy CreateFile call
1406 which might return a valid HANDLE without having actually opened
1408 The only known file system to date is the SUN NFS Solstice Client 3.1
1409 which returns a valid handle when trying to open a file in a nonexistent
1411 if (pc.has_buggy_open () && !pc.exists ())
1413 debug_printf ("Buggy open detected.");
1419 ino = get_ino_by_handle (pc, get_handle ());
1420 /* A unique ID is necessary to recognize fhandler entries which are
1421 duplicated by dup(2) or fork(2). */
1422 AllocateLocallyUniqueId ((PLUID) &unique_id);
1425 syscall_printf ("%d = fhandler_disk_file::open (%S, %p)", res,
1426 pc.get_nt_native_path (), flags);
1431 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
1434 _off64_t curpos = lseek (0, SEEK_CUR);
1435 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1439 size_t tmp_count = count;
1440 read (buf, tmp_count);
1441 if (lseek (curpos, SEEK_SET) >= 0)
1442 res = (ssize_t) tmp_count;
1446 debug_printf ("%d = pread (%p, %d, %d)\n", res, buf, count, offset);
1451 fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
1454 _off64_t curpos = lseek (0, SEEK_CUR);
1455 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1459 res = (ssize_t) write (buf, count);
1460 if (lseek (curpos, SEEK_SET) < 0)
1463 debug_printf ("%d = pwrite (%p, %d, %d)\n", res, buf, count, offset);
1468 fhandler_disk_file::mkdir (mode_t mode)
1471 SECURITY_ATTRIBUTES sa = sec_none_nih;
1474 OBJECT_ATTRIBUTES attr;
1476 PFILE_FULL_EA_INFORMATION p = NULL;
1479 if (pc.fs_is_nfs ())
1481 /* When creating a dir on an NFS share, we have to set the
1482 file mode by writing a NFS fattr3 structure with the
1483 correct mode bits set. */
1484 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1486 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1487 p->NextEntryOffset = 0;
1489 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1490 p->EaValueLength = sizeof (fattr3);
1491 strcpy (p->EaName, NFS_V3_ATTR);
1492 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1493 memset (nfs_attr, 0, sizeof (fattr3));
1494 nfs_attr->type = NF3DIR;
1495 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1497 status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
1498 pc.get_object_attr (attr, sa), &io, NULL,
1499 FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
1501 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1502 | FILE_OPEN_FOR_BACKUP_INTENT,
1504 if (NT_SUCCESS (status))
1507 set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
1508 S_JUSTCREATED | S_IFDIR
1509 | ((mode & 07777) & ~cygheap->umask));
1514 __seterrno_from_nt_status (status);
1520 fhandler_disk_file::rmdir ()
1522 extern NTSTATUS unlink_nt (path_conv &pc);
1526 set_errno (ENOTDIR);
1535 NTSTATUS status = unlink_nt (pc);
1537 /* Check for existence of remote dirs after trying to delete them.
1539 - Sometimes SMB indicates failure when it really succeeds.
1540 - Removeing a directory on a samba drive doesn't return an error if the
1541 directory can't be removed because it's not empty. */
1544 OBJECT_ATTRIBUTES attr;
1545 FILE_BASIC_INFORMATION fbi;
1547 if (NT_SUCCESS (NtQueryAttributesFile
1548 (pc.get_object_attr (attr, sec_none_nih), &fbi)))
1549 status = STATUS_DIRECTORY_NOT_EMPTY;
1551 status = STATUS_SUCCESS;
1553 if (!NT_SUCCESS (status))
1555 __seterrno_from_nt_status (status);
1561 /* This is the minimal number of entries which fit into the readdir cache.
1562 The number of bytes allocated by the cache is determined by this number,
1563 To tune caching, just tweak this number. To get a feeling for the size,
1564 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1566 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1568 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1569 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1570 + (NAME_MAX + 1) * sizeof (WCHAR)))
1574 char __cache[DIR_BUF_SIZE]; /* W2K needs this buffer 8 byte aligned. */
1578 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1579 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1581 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1584 fhandler_disk_file::opendir (int fd)
1590 set_errno (ENOTDIR);
1591 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1593 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1599 else if ((dir->__d_dirent =
1600 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1608 if (cfd < 0 && fd < 0)
1611 dir->__d_dirent->__d_version = __DIRENT_VERSION;
1612 dir->__d_cookie = __DIRENT_COOKIE;
1613 dir->__handle = INVALID_HANDLE_VALUE;
1614 dir->__d_position = 0;
1615 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1616 ? dirent_isroot : 0;
1617 dir->__d_internal = (unsigned) new __DIR_mounts (get_name ());
1618 d_cachepos (dir) = 0;
1620 if (!pc.iscygdrive ())
1624 /* opendir() case. Initialize with given directory name and
1625 NULL directory handle. */
1626 OBJECT_ATTRIBUTES attr;
1630 status = NtOpenFile (&get_handle (),
1631 SYNCHRONIZE | FILE_LIST_DIRECTORY,
1632 pc.get_object_attr (attr, sec_none_nih),
1633 &io, FILE_SHARE_VALID_FLAGS,
1634 FILE_SYNCHRONOUS_IO_NONALERT
1635 | FILE_OPEN_FOR_BACKUP_INTENT
1636 | FILE_DIRECTORY_FILE);
1637 if (!NT_SUCCESS (status))
1639 __seterrno_from_nt_status (status);
1644 /* FileIdBothDirectoryInformation is apparently unsupported on
1645 XP when accessing directories on UDF. When trying to use it
1646 so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1647 It's not clear if the call isn't also unsupported on other
1648 OS/FS combinations (say, Win2K/CDFS or so). Instead of
1649 testing in readdir for yet another error code, let's use
1650 FileIdBothDirectoryInformation only on filesystems supporting
1651 persistent ACLs, FileDirectoryInformation otherwise.
1653 NFS clients hide dangling symlinks from directory queries,
1654 unless you use the FileNamesInformation info class.
1655 On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
1656 works fine, but only if the NFS share is mounted to a drive
1657 letter. TODO: We don't test that here for now, but it might
1658 be worth to test if there's a speed gain in using
1659 FileIdBothDirectoryInformation, because it doesn't require to
1660 open the file to read the inode number. */
1661 if (pc.hasgood_inode ())
1663 dir->__flags |= dirent_set_d_ino;
1664 if (pc.fs_is_nfs ())
1665 dir->__flags |= dirent_nfs_d_ino;
1666 else if (wincap.has_fileid_dirinfo ()
1667 && !pc.has_buggy_fileid_dirinfo ())
1668 dir->__flags |= dirent_get_d_ino;
1675 /* Filling cfd with `this' (aka storing this in the file
1676 descriptor table should only happen after it's clear that
1677 opendir doesn't fail, otherwise we end up cfree'ing the
1678 fhandler twice, once in opendir() in dir.cc, the second
1679 time on exit. Nasty, nasty... */
1682 if (pc.iscygdrive ())
1683 cfd->nohandle (true);
1685 set_close_on_exec (true);
1690 syscall_printf ("%p = opendir (%s)", res, get_name ());
1694 delete d_mounts (dir);
1696 free (dir->__d_dirent);
1698 free (dir->__d_dirname);
1705 readdir_get_ino (const char *path, bool dot_dot)
1710 OBJECT_ATTRIBUTES attr;
1716 fname = (char *) alloca (strlen (path) + 4);
1717 char *c = stpcpy (fname, path);
1723 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN);
1724 if (pc.isspecial ())
1726 if (!stat_worker (pc, &st))
1729 else if (!pc.hasgood_inode ())
1730 ino = hash_path_name (0, pc.get_nt_native_path ());
1731 else if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1732 pc.get_object_attr (attr, sec_none_nih),
1733 &io, FILE_SHARE_VALID_FLAGS,
1734 FILE_OPEN_FOR_BACKUP_INTENT
1735 | (pc.is_rep_symlink ()
1736 ? FILE_OPEN_REPARSE_POINT : 0))))
1738 ino = get_ino_by_handle (pc, hdl);
1740 ino = hash_path_name (0, pc.get_nt_native_path ());
1747 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1748 DWORD attr, PUNICODE_STRING fname)
1753 if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
1756 return geterrno_from_win_error (w32_err);
1759 dir->__flags &= ~dirent_set_d_ino;
1762 /* Set d_type if type can be determined from file attributes.
1763 FILE_ATTRIBUTE_SYSTEM ommitted to leave DT_UNKNOWN for old symlinks.
1764 For new symlinks, d_type will be reset to DT_UNKNOWN below. */
1766 !(attr & ( ~FILE_ATTRIBUTE_VALID_FLAGS
1767 | FILE_ATTRIBUTE_SYSTEM
1768 | FILE_ATTRIBUTE_REPARSE_POINT)))
1770 if (attr & FILE_ATTRIBUTE_DIRECTORY)
1771 de->d_type = DT_DIR;
1773 de->d_type = DT_REG;
1776 /* Check for directory reparse point. These are potential volume mount
1777 points which have another inode than the underlying directory. */
1778 if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1779 == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1782 OBJECT_ATTRIBUTES attr;
1785 InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
1786 get_handle (), NULL);
1787 if (is_volume_mountpoint (&attr)
1788 && (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
1789 FILE_SHARE_VALID_FLAGS,
1790 FILE_OPEN_FOR_BACKUP_INTENT))))
1792 de->d_ino = get_ino_by_handle (pc, reph);
1797 /* Check for Windows shortcut. If it's a Cygwin or U/WIN
1798 symlink, drop the .lnk suffix. */
1799 if ((attr & FILE_ATTRIBUTE_READONLY) && fname->Length > 4 * sizeof (WCHAR))
1801 UNICODE_STRING uname;
1803 RtlInitCountedUnicodeString (&uname,
1805 + fname->Length / sizeof (WCHAR) - 4,
1806 4 * sizeof (WCHAR));
1807 if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
1810 UNICODE_STRING fbuf;
1813 RtlCopyUnicodeString (&fbuf, pc.get_nt_native_path ());
1814 RtlAppendUnicodeToString (&fbuf, L"\\");
1815 RtlAppendUnicodeStringToString (&fbuf, fname);
1816 fbuf.Buffer += 4; /* Skip leading \??\ */
1817 fbuf.Length -= 4 * sizeof (WCHAR);
1818 if (fbuf.Buffer[1] != L':') /* UNC path */
1820 *(fbuf.Buffer += 2) = L'\\';
1821 fbuf.Length -= 2 * sizeof (WCHAR);
1823 path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
1824 if (fpath.issymlink () || fpath.is_fs_special ())
1826 fname->Length -= 4 * sizeof (WCHAR);
1827 de->d_type = DT_UNKNOWN;
1832 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
1833 fname->Length / sizeof (WCHAR));
1835 /* Don't try to optimize relative to dir->__d_position. On several
1836 filesystems it's no safe bet that "." and ".." entries always
1838 if (de->d_name[0] == '.')
1840 if (de->d_name[1] == '\0')
1841 dir->__flags |= dirent_saw_dot;
1842 else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
1843 dir->__flags |= dirent_saw_dot_dot;
1849 fhandler_disk_file::readdir (DIR *dir, dirent *de)
1852 NTSTATUS status = STATUS_SUCCESS;
1853 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
1855 ULONG FileNameLength;
1856 ULONG FileAttributes = 0;
1858 UNICODE_STRING fname;
1860 /* d_cachepos always refers to the next cache entry to use. If it's 0
1861 we must reload the cache. */
1862 if (d_cachepos (dir) == 0)
1864 if ((dir->__flags & dirent_get_d_ino))
1866 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1867 d_cache (dir), DIR_BUF_SIZE,
1868 FileIdBothDirectoryInformation,
1869 FALSE, NULL, dir->__d_position == 0);
1870 /* FileIdBothDirectoryInformation isn't supported for remote drives
1871 on NT4 and 2K systems, and it's also not supported on 2K at all,
1872 when accessing network drives on any remote OS. There are also
1873 hacked versions of Samba 3.0.x out there (Debian-based it seems),
1874 which return STATUS_NOT_SUPPORTED rather than handling this info
1875 class. We just fall back to using a standard directory query in
1876 this case and note this case using the dirent_get_d_ino flag. */
1877 if (status == STATUS_INVALID_LEVEL
1878 || status == STATUS_NOT_SUPPORTED
1879 || status == STATUS_INVALID_PARAMETER
1880 || status == STATUS_INVALID_INFO_CLASS)
1881 dir->__flags &= ~dirent_get_d_ino;
1882 /* Something weird happens on Samba up to version 3.0.21c, which is
1883 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
1884 nicely, but only up to the 128th entry in the directory. After
1885 reaching this entry, the next call to NtQueryDirectoryFile
1886 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
1887 Why should we care, we can just switch to FileDirectoryInformation,
1888 isn't it? Nope! The next call to
1889 NtQueryDirectoryFile(FileDirectoryInformation)
1890 actually returns STATUS_NO_MORE_FILES, regardless how many files
1891 are left unread in the directory. This does not happen when using
1892 FileDirectoryInformation right from the start, but since
1893 we can't decide whether the server we're talking with has this
1894 bug or not, we end up serving Samba shares always in the slow
1895 mode using FileDirectoryInformation. So, what we do here is
1896 to implement the solution suggested by Andrew Tridgell, we just
1897 reread all entries up to dir->d_position using
1898 FileDirectoryInformation.
1899 However, We do *not* mark this server as broken and fall back to
1900 using FileDirectoryInformation further on. This would slow
1901 down every access to such a server, even for directories under
1902 128 entries. Also, bigger dirs only suffer from one additional
1903 call per full directory scan, which shouldn't be too big a hit.
1904 This can easily be changed if necessary. */
1905 if (status == STATUS_INVALID_LEVEL && dir->__d_position)
1907 d_cachepos (dir) = 0;
1908 for (int cnt = 0; cnt < dir->__d_position; ++cnt)
1910 if (d_cachepos (dir) == 0)
1912 status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
1913 NULL, &io, d_cache (dir),
1915 FileDirectoryInformation,
1916 FALSE, NULL, cnt == 0);
1917 if (!NT_SUCCESS (status))
1920 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
1921 + d_cachepos (dir));
1922 if (buf->NextEntryOffset == 0)
1923 d_cachepos (dir) = 0;
1925 d_cachepos (dir) += buf->NextEntryOffset;
1930 if (!(dir->__flags & dirent_get_d_ino))
1931 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1932 d_cache (dir), DIR_BUF_SIZE,
1933 (dir->__flags & dirent_nfs_d_ino)
1934 ? FileNamesInformation
1935 : FileDirectoryInformation,
1936 FALSE, NULL, dir->__d_position == 0);
1941 if (status == STATUS_NO_MORE_FILES)
1943 else if (!NT_SUCCESS (status))
1944 debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
1945 status, RtlNtStatusToDosError (status));
1948 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
1949 if (buf->NextEntryOffset == 0)
1950 d_cachepos (dir) = 0;
1952 d_cachepos (dir) += buf->NextEntryOffset;
1953 if ((dir->__flags & dirent_get_d_ino))
1955 FileName = buf->FileName;
1956 FileNameLength = buf->FileNameLength;
1957 FileAttributes = buf->FileAttributes;
1958 if ((dir->__flags & dirent_set_d_ino))
1959 de->d_ino = buf->FileId.QuadPart;
1961 else if ((dir->__flags & dirent_nfs_d_ino))
1963 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
1964 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
1968 FileName = ((PFILE_DIRECTORY_INFORMATION) buf)->FileName;
1969 FileNameLength = ((PFILE_DIRECTORY_INFORMATION) buf)->FileNameLength;
1970 FileAttributes = ((PFILE_DIRECTORY_INFORMATION) buf)->FileAttributes;
1972 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
1973 de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
1974 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
1976 /* Don't try to optimize relative to dir->__d_position. On several
1977 filesystems it's no safe bet that "." and ".." entries always
1979 if (FileNameLength == sizeof (WCHAR) && FileName[0] == '.')
1980 de->d_ino = get_ino_by_handle (pc, get_handle ());
1981 else if (FileNameLength == 2 * sizeof (WCHAR)
1982 && FileName[0] == L'.' && FileName[1] == L'.')
1984 if (!(dir->__flags & dirent_isroot))
1985 de->d_ino = readdir_get_ino (get_name (), true);
1987 de->d_ino = get_ino_by_handle (pc, get_handle ());
1991 OBJECT_ATTRIBUTES attr;
1995 InitializeObjectAttributes (&attr, &fname,
1996 pc.objcaseinsensitive (),
1997 get_handle (), NULL);
1998 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
1999 NtOpenFile here returns the inode number of the symlink target,
2000 rather than the inode number of the symlink itself.
2002 Worse, trying to open a symlink without setting the special
2003 "ActOnSymlink" EA triggers a bug in Windows 7 which results
2004 in a timeout of up to 20 seconds, followed by two exceptions
2007 Since both results are far from desirable, we open symlinks
2008 on NFS so that we get the right inode and a happy W7.
2009 And, since some filesystems choke on the EAs, we don't
2010 use them unconditionally. */
2011 f_status = (dir->__flags & dirent_nfs_d_ino)
2012 ? NtCreateFile (&hdl, READ_CONTROL, &attr, &io,
2013 NULL, 0, FILE_SHARE_VALID_FLAGS,
2014 FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT,
2015 &nfs_aol_ffei, sizeof nfs_aol_ffei)
2016 : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
2017 FILE_SHARE_VALID_FLAGS,
2018 FILE_OPEN_FOR_BACKUP_INTENT
2019 | FILE_OPEN_REPARSE_POINT);
2020 if (NT_SUCCESS (f_status))
2022 de->d_ino = get_ino_by_handle (pc, hdl);
2026 /* Untrusted file system. Don't try to fetch inode number again. */
2028 dir->__flags &= ~dirent_set_d_ino;
2032 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
2033 buf ? FileAttributes : 0, &fname)))
2034 dir->__d_position++;
2035 else if (!(dir->__flags & dirent_saw_dot))
2037 strcpy (de->d_name , ".");
2038 de->d_ino = get_ino_by_handle (pc, get_handle ());
2039 dir->__d_position++;
2040 dir->__flags |= dirent_saw_dot;
2043 else if (!(dir->__flags & dirent_saw_dot_dot))
2045 strcpy (de->d_name , "..");
2046 if (!(dir->__flags & dirent_isroot))
2047 de->d_ino = readdir_get_ino (get_name (), true);
2049 de->d_ino = get_ino_by_handle (pc, get_handle ());
2050 dir->__d_position++;
2051 dir->__flags |= dirent_saw_dot_dot;
2055 syscall_printf ("%d = readdir (%p, %p) (L\"%lS\" > \"%ls\")", res, dir, &de,
2056 res ? NULL : &fname, res ? "***" : de->d_name);
2061 fhandler_disk_file::telldir (DIR *dir)
2063 return dir->__d_position;
2067 fhandler_disk_file::seekdir (DIR *dir, _off64_t loc)
2070 while (loc > dir->__d_position)
2071 if (!::readdir (dir))
2076 fhandler_disk_file::rewinddir (DIR *dir)
2078 d_cachepos (dir) = 0;
2079 if (wincap.has_buggy_restart_scan () && isremote ())
2081 /* This works around a W2K bug. The RestartScan parameter in calls
2082 to NtQueryDirectoryFile on remote shares is ignored, thus
2083 resulting in not being able to rewind on remote shares. By
2084 reopening the directory, we get a fresh new directory pointer. */
2085 OBJECT_ATTRIBUTES attr;
2090 InitializeObjectAttributes (&attr, &ro_u_empty, pc.objcaseinsensitive (),
2091 get_handle (), NULL);
2092 status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2093 &attr, &io, FILE_SHARE_VALID_FLAGS,
2094 FILE_SYNCHRONOUS_IO_NONALERT
2095 | FILE_OPEN_FOR_BACKUP_INTENT
2096 | FILE_DIRECTORY_FILE);
2097 if (!NT_SUCCESS (stat))
2098 debug_printf ("Unable to reopen dir %s, NT error: %p",
2099 get_name (), status);
2102 NtClose (get_handle ());
2103 set_io_handle (new_dir);
2106 dir->__d_position = 0;
2107 d_mounts (dir)->rewind ();
2111 fhandler_disk_file::closedir (DIR *dir)
2116 delete d_mounts (dir);
2119 else if (get_handle () == INVALID_HANDLE_VALUE)
2124 else if (!NT_SUCCESS (status = NtClose (get_handle ())))
2126 __seterrno_from_nt_status (status);
2129 syscall_printf ("%d = closedir (%p, %s)", res, dir, get_name ());
2133 fhandler_cygdrive::fhandler_cygdrive () :
2134 fhandler_disk_file (), ndrives (0), pdrive (NULL)
2139 fhandler_cygdrive::open (int flags, mode_t mode)
2141 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2146 if (flags & O_WRONLY)
2158 fhandler_cygdrive::close ()
2164 fhandler_cygdrive::set_drives ()
2166 pdrive = pdrive_buf;
2167 ndrives = GetLogicalDriveStrings (sizeof pdrive_buf, pdrive_buf) / DRVSZ;
2171 fhandler_cygdrive::fstat (struct __stat64 *buf)
2173 fhandler_base::fstat (buf);
2175 buf->st_mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
2178 char flptst[] = "X:";
2180 for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
2181 if (is_floppy ((flptst[0] = *p, flptst))
2182 || GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
2184 buf->st_nlink = n + 2;
2189 fhandler_cygdrive::opendir (int fd)
2193 dir = fhandler_disk_file::opendir (fd);
2194 if (dir && !ndrives)
2201 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2203 char flptst[] = "X:";
2207 if (!pdrive || !*pdrive)
2209 if (!(dir->__flags & dirent_saw_dot))
2211 de->d_name[0] = '.';
2212 de->d_name[1] = '\0';
2217 if (!is_floppy ((flptst[0] = *pdrive, flptst))
2218 && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2220 pdrive = strchr (pdrive, '\0') + 1;
2222 *de->d_name = cyg_tolower (*pdrive);
2223 de->d_name[1] = '\0';
2224 user_shared->warned_msdos = true;
2225 de->d_ino = readdir_get_ino (pdrive, false);
2226 dir->__d_position++;
2227 pdrive = strchr (pdrive, '\0') + 1;
2228 syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2233 fhandler_cygdrive::rewinddir (DIR *dir)
2235 pdrive = pdrive_buf;
2236 dir->__d_position = 0;
2240 fhandler_cygdrive::closedir (DIR *dir)
2242 pdrive = pdrive_buf;