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);
676 syscall_printf ("0 = fstat (, %p) st_atime=%x st_size=%D, st_mode=%p, st_ino=%D, sizeof=%d",
677 buf, buf->st_atime, buf->st_size, buf->st_mode,
678 buf->st_ino, sizeof (*buf));
683 fhandler_disk_file::fstat (struct __stat64 *buf)
685 return fstat_fs (buf);
689 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
691 int ret = -1, opened = 0;
694 FILE_FS_FULL_SIZE_INFORMATION full_fsi;
695 FILE_FS_SIZE_INFORMATION fsi;
696 HANDLE fh = get_handle ();
700 OBJECT_ATTRIBUTES attr;
701 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
702 pc.get_object_attr (attr, sec_none_nih),
703 &io, FILE_SHARE_VALID_FLAGS,
704 FILE_OPEN_FOR_BACKUP_INTENT));
707 /* Can't open file. Try again with parent dir. */
708 UNICODE_STRING dirname;
709 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
710 attr.ObjectName = &dirname;
711 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
712 FILE_SHARE_VALID_FLAGS,
713 FILE_OPEN_FOR_BACKUP_INTENT));
719 sfs->f_files = ULONG_MAX;
720 sfs->f_ffree = ULONG_MAX;
721 sfs->f_favail = ULONG_MAX;
722 sfs->f_fsid = pc.fs_serial_number ();
723 sfs->f_flag = pc.fs_flags ();
724 sfs->f_namemax = pc.fs_name_len ();
725 /* Get allocation related information. Try to get "full" information
726 first, which is only available since W2K. If that fails, try to
727 retrieve normal allocation information. */
728 status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
729 FileFsFullSizeInformation);
730 if (NT_SUCCESS (status))
732 sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
733 sfs->f_frsize = sfs->f_bsize;
734 sfs->f_blocks = full_fsi.TotalAllocationUnits.LowPart;
735 sfs->f_bfree = full_fsi.ActualAvailableAllocationUnits.LowPart;
736 sfs->f_bavail = full_fsi.CallerAvailableAllocationUnits.LowPart;
737 if (sfs->f_bfree > sfs->f_bavail)
739 /* Quotas active. We can't trust TotalAllocationUnits. */
740 NTFS_VOLUME_DATA_BUFFER nvdb;
742 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
743 FSCTL_GET_NTFS_VOLUME_DATA,
744 NULL, 0, &nvdb, sizeof nvdb);
745 if (!NT_SUCCESS (status))
746 debug_printf ("%p = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
747 status, pc.get_nt_native_path ());
749 sfs->f_blocks = nvdb.TotalClusters.QuadPart;
755 status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
756 FileFsSizeInformation);
757 if (!NT_SUCCESS (status))
759 __seterrno_from_nt_status (status);
762 sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
763 sfs->f_frsize = sfs->f_bsize;
764 sfs->f_blocks = fsi.TotalAllocationUnits.LowPart;
765 sfs->f_bfree = fsi.AvailableAllocationUnits.LowPart;
766 sfs->f_bavail = sfs->f_bfree;
772 syscall_printf ("%d = fstatvfs (%s, %p)", ret, get_name (), sfs);
777 fhandler_disk_file::fchmod (mode_t mode)
779 extern int chmod_device (path_conv& pc, mode_t mode);
785 if (pc.is_fs_special ())
786 return chmod_device (pc, mode);
790 query_open (query_write_control);
791 if (!(oret = open (O_BINARY, 0)))
793 /* Need WRITE_DAC|WRITE_OWNER to write ACLs. */
796 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
797 query_open (query_write_attributes);
798 if (!(oret = open (O_BINARY, 0)))
805 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
806 Only type and mode have to be set. Apparently type isn't checked
807 for consistency, so it's sufficent to set it to NF3REG all the time. */
809 FILE_FULL_EA_INFORMATION ffei;
810 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
812 ffei_buf.ffei.NextEntryOffset = 0;
813 ffei_buf.ffei.Flags = 0;
814 ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
815 ffei_buf.ffei.EaValueLength = sizeof (fattr3);
816 strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
817 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
818 + ffei_buf.ffei.EaNameLength + 1);
819 memset (nfs_attr, 0, sizeof (fattr3));
820 nfs_attr->type = NF3REG;
821 nfs_attr->mode = mode;
822 status = NtSetEaFile (get_handle (), &io,
823 &ffei_buf.ffei, sizeof ffei_buf);
824 if (!NT_SUCCESS (status))
825 __seterrno_from_nt_status (status);
835 if (!set_file_attribute (get_handle (), pc,
836 ILLEGAL_UID, ILLEGAL_GID, mode))
840 /* If the mode has any write bits set, the DOS R/O flag is in the way. */
841 if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
842 pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
843 else if (!pc.has_acls ()) /* Never set DOS R/O if security is used. */
844 pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
846 pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
848 status = NtSetAttributesFile (get_handle (), pc.file_attributes ());
849 /* MVFS needs a good amount of kicking to be convinced that it has to write
850 back metadata changes and to invalidate the cached metadata information
851 stored for the given handle. This method to open a second handle to
852 the file and write the same metadata information twice has been found
853 experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
854 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !oret)
856 OBJECT_ATTRIBUTES attr;
859 InitializeObjectAttributes (&attr, &ro_u_empty, 0, get_handle (), NULL);
860 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
861 FILE_SHARE_VALID_FLAGS,
862 FILE_OPEN_FOR_BACKUP_INTENT)))
864 NtSetAttributesFile (fh, pc.file_attributes ());
868 /* Correct NTFS security attributes have higher priority */
871 if (!NT_SUCCESS (status))
872 __seterrno_from_nt_status (status);
885 fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
891 /* fake - if not supported, pretend we're like win95
892 where it just works */
893 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
899 query_open (query_write_control);
900 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
908 int res = get_file_attribute (get_handle (), pc, &attrib, &old_uid, NULL);
911 /* Typical Windows default ACLs can contain permissions for one
912 group, while being owned by another user/group. The permission
913 bits returned above are pretty much useless then. Creating a
914 new ACL with these useless permissions results in a potentially
915 broken symlink. So what we do here is to set the underlying
916 permissions of symlinks to a sensible value which allows the
917 world to read the symlink and only the new owner to change it. */
919 attrib = S_IFLNK | STD_RBITS | STD_WBITS;
920 res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
921 /* If you're running a Samba server which has no winbidd running, the
922 uid<->SID mapping is disfunctional. Even trying to chown to your
923 own account fails since the account used on the server is the UNIX
924 account which gets used for the standard user mapping. This is a
925 default mechanism which doesn't know your real Windows SID.
926 There are two possible error codes in different Samba releases for
927 this situation, one of them is unfortunately the not very significant
928 STATUS_ACCESS_DENIED. Instead of relying on the error codes, we're
929 using the below very simple heuristic. If set_file_attribute failed,
930 and the original user account was either already unknown, or one of
931 the standard UNIX accounts, we're faking success. */
932 if (res == -1 && pc.fs_is_samba ())
936 if (old_uid == ILLEGAL_UID
937 || (sid.getfrompw (internal_getpwuid (old_uid))
938 && EqualPrefixSid (sid, well_known_samba_unix_user_fake_sid)))
940 debug_printf ("Faking chown worked on standalone Samba");
952 fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp)
965 /* Open for writing required to be able to set ctime
966 (even though setting the ACL is just pretended). */
968 oret = open (O_WRONLY | O_BINARY, 0);
974 else if (nentries < MIN_ACL_ENTRIES)
980 query_open (query_read_attributes);
981 oret = open (O_BINARY, 0);
983 if ((oret && !fstat_by_handle (&st))
984 || !fstat_by_name (&st))
986 aclbufp[0].a_type = USER_OBJ;
987 aclbufp[0].a_id = st.st_uid;
988 aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
989 aclbufp[1].a_type = GROUP_OBJ;
990 aclbufp[1].a_id = st.st_gid;
991 aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
992 aclbufp[2].a_type = OTHER_OBJ;
993 aclbufp[2].a_id = ILLEGAL_GID;
994 aclbufp[2].a_perm = st.st_mode & S_IRWXO;
995 aclbufp[3].a_type = CLASS_OBJ;
996 aclbufp[3].a_id = ILLEGAL_GID;
997 aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
998 res = MIN_ACL_ENTRIES;
1003 res = MIN_ACL_ENTRIES;
1014 query_open (cmd == SETACL ? query_write_control : query_read_control);
1015 if (!(oret = open (O_BINARY, 0)))
1017 if (cmd == GETACL || cmd == GETACLCNT)
1018 goto cant_access_acl;
1025 if (!aclsort32 (nentries, 0, aclbufp))
1028 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1032 FILE_BASIC_INFORMATION fbi;
1033 fbi.CreationTime.QuadPart
1034 = fbi.LastAccessTime.QuadPart
1035 = fbi.LastWriteTime.QuadPart
1036 = fbi.ChangeTime.QuadPart = 0LL;
1037 fbi.FileAttributes = (pc.file_attributes ()
1038 & ~FILE_ATTRIBUTE_READONLY)
1039 ?: FILE_ATTRIBUTE_NORMAL;
1040 NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1041 FileBasicInformation);
1049 res = getacl (get_handle (), pc, nentries, aclbufp);
1052 res = getacl (get_handle (), pc, 0, NULL);
1067 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1069 if (pc.is_fs_special ())
1071 set_errno (ENOTSUP);
1074 return read_ea (get_handle (), pc, name, (char *) value, size);
1078 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1081 if (pc.is_fs_special ())
1083 set_errno (ENOTSUP);
1086 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1090 fhandler_disk_file::fadvise (_off64_t offset, _off64_t length, int advice)
1092 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1098 /* Windows only supports advice flags for the whole file. We're using
1099 a simplified test here so that we don't have to ask for the actual
1100 file size. Length == 0 means all bytes starting at offset anyway.
1101 So we only actually follow the advice, if it's given for offset == 0. */
1105 /* We only support normal and sequential mode for now. Everything which
1106 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1107 if (advice != POSIX_FADV_SEQUENTIAL)
1108 advice = POSIX_FADV_NORMAL;
1111 FILE_MODE_INFORMATION fmi;
1112 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1114 FileModeInformation);
1115 if (!NT_SUCCESS (status))
1116 __seterrno_from_nt_status (status);
1119 fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1120 if (advice == POSIX_FADV_SEQUENTIAL)
1121 fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1122 status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1123 FileModeInformation);
1124 if (NT_SUCCESS (status))
1126 __seterrno_from_nt_status (status);
1133 fhandler_disk_file::ftruncate (_off64_t length, bool allow_truncate)
1137 if (length < 0 || !get_handle ())
1139 else if (pc.isdir ())
1141 else if (!(get_access () & GENERIC_WRITE))
1147 FILE_STANDARD_INFORMATION fsi;
1148 FILE_END_OF_FILE_INFORMATION feofi;
1150 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1151 FileStandardInformation);
1152 if (!NT_SUCCESS (status))
1154 __seterrno_from_nt_status (status);
1158 /* If called through posix_fallocate, silently succeed if length
1159 is less than the file's actual length. */
1160 if (!allow_truncate && length < fsi.EndOfFile.QuadPart)
1163 feofi.EndOfFile.QuadPart = length;
1164 /* Create sparse files only when called through ftruncate, not when
1165 called through posix_fallocate. */
1167 && (pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES)
1168 && length >= fsi.EndOfFile.QuadPart + (128 * 1024))
1170 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1171 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1172 syscall_printf ("%p = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1173 status, pc.get_nt_native_path ());
1175 status = NtSetInformationFile (get_handle (), &io,
1176 &feofi, sizeof feofi,
1177 FileEndOfFileInformation);
1178 if (!NT_SUCCESS (status))
1179 __seterrno_from_nt_status (status);
1187 fhandler_disk_file::link (const char *newpath)
1189 size_t nlen = strlen (newpath);
1190 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
1193 set_errno (newpc.error);
1197 if (newpc.exists ())
1199 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1204 if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
1210 char new_buf[nlen + 5];
1213 if (pc.is_lnk_special ())
1215 /* Shortcut hack. */
1216 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1218 newpc.check (newpath, PC_SYM_NOFOLLOW);
1220 else if (!pc.isdir ()
1222 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1224 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1227 /* Executable hack. */
1228 stpcpy (stpcpy (new_buf, newpath), ".exe");
1230 newpc.check (newpath, PC_SYM_NOFOLLOW);
1236 OBJECT_ATTRIBUTES attr;
1238 status = NtOpenFile (&fh, READ_CONTROL,
1239 pc.get_object_attr (attr, sec_none_nih), &io,
1240 FILE_SHARE_VALID_FLAGS,
1241 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
1242 if (!NT_SUCCESS (status))
1244 __seterrno_from_nt_status (status);
1247 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1248 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1249 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1250 pfli->ReplaceIfExists = FALSE;
1251 pfli->RootDirectory = NULL;
1252 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1253 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1255 if (!NT_SUCCESS (status))
1257 if (status == STATUS_INVALID_DEVICE_REQUEST)
1259 /* FS doesn't support hard links. Linux returns EPERM. */
1265 __seterrno_from_nt_status (status);
1273 fhandler_disk_file::utimens (const struct timespec *tvp)
1275 return utimens_fs (tvp);
1279 fhandler_base::utimens_fs (const struct timespec *tvp)
1281 struct timespec timeofday;
1282 struct timespec tmp[2];
1283 bool closeit = false;
1287 query_open (query_write_attributes);
1288 if (!open_fs (O_BINARY, 0))
1290 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1291 to change the timestamps. Unfortunately it's not sufficient for a
1292 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1293 for writing, though this fails for R/O files of course. */
1294 query_open (no_query);
1295 if (!open_fs (O_WRONLY | O_BINARY, 0))
1297 syscall_printf ("Opening file failed");
1304 gettimeofday (reinterpret_cast<struct timeval *> (&timeofday), 0);
1305 timeofday.tv_nsec *= 1000;
1307 tmp[1] = tmp[0] = timeofday;
1310 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1311 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1316 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1317 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1319 debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
1322 FILE_BASIC_INFORMATION fbi;
1324 fbi.CreationTime.QuadPart = 0LL;
1325 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1326 timespec_to_filetime (&tmp[0], (LPFILETIME) &fbi.LastAccessTime);
1327 timespec_to_filetime (&tmp[1], (LPFILETIME) &fbi.LastWriteTime);
1328 fbi.ChangeTime.QuadPart = 0LL;
1329 fbi.FileAttributes = 0;
1330 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1331 FileBasicInformation);
1332 /* For this special case for MVFS see the comment in
1333 fhandler_disk_file::fchmod. */
1334 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
1336 OBJECT_ATTRIBUTES attr;
1339 InitializeObjectAttributes (&attr, &ro_u_empty, 0, get_handle (), NULL);
1340 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
1341 FILE_SHARE_VALID_FLAGS,
1342 FILE_OPEN_FOR_BACKUP_INTENT)))
1344 NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1345 FileBasicInformation);
1351 /* Opening a directory on a 9x share from a NT machine works(!), but
1352 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1353 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1355 __seterrno_from_nt_status (status);
1361 fhandler_disk_file::fhandler_disk_file () :
1366 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1373 fhandler_disk_file::open (int flags, mode_t mode)
1375 return open_fs (flags, mode);
1379 fhandler_base::open_fs (int flags, mode_t mode)
1381 /* Unfortunately NT allows to open directories for writing, but that's
1382 disallowed according to SUSv3. */
1383 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1389 int res = fhandler_base::open (flags | O_DIROPEN, mode);
1393 /* This is for file systems known for having a buggy CreateFile call
1394 which might return a valid HANDLE without having actually opened
1396 The only known file system to date is the SUN NFS Solstice Client 3.1
1397 which returns a valid handle when trying to open a file in a nonexistent
1399 if (pc.has_buggy_open () && !pc.exists ())
1401 debug_printf ("Buggy open detected.");
1407 ino = get_ino_by_handle (pc, get_handle ());
1408 /* A unique ID is necessary to recognize fhandler entries which are
1409 duplicated by dup(2) or fork(2). */
1410 AllocateLocallyUniqueId ((PLUID) &unique_id);
1413 syscall_printf ("%d = fhandler_disk_file::open (%S, %p)", res,
1414 pc.get_nt_native_path (), flags);
1419 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
1422 _off64_t curpos = lseek (0, SEEK_CUR);
1423 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1427 size_t tmp_count = count;
1428 read (buf, tmp_count);
1429 if (lseek (curpos, SEEK_SET) >= 0)
1430 res = (ssize_t) tmp_count;
1434 debug_printf ("%d = pread (%p, %d, %d)\n", res, buf, count, offset);
1439 fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
1442 _off64_t curpos = lseek (0, SEEK_CUR);
1443 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1447 res = (ssize_t) write (buf, count);
1448 if (lseek (curpos, SEEK_SET) < 0)
1451 debug_printf ("%d = pwrite (%p, %d, %d)\n", res, buf, count, offset);
1456 fhandler_disk_file::mkdir (mode_t mode)
1459 SECURITY_ATTRIBUTES sa = sec_none_nih;
1460 security_descriptor sd;
1462 /* See comments in fhander_base::open () for an explanation why we defer
1463 setting security attributes on remote files. */
1464 if (has_acls () && !pc.isremote ())
1465 set_security_attribute (pc, S_IFDIR | ((mode & 07777) & ~cygheap->umask),
1470 OBJECT_ATTRIBUTES attr;
1472 PFILE_FULL_EA_INFORMATION p = NULL;
1475 if (pc.fs_is_nfs ())
1477 /* When creating a dir on an NFS share, we have to set the
1478 file mode by writing a NFS fattr3 structure with the
1479 correct mode bits set. */
1480 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1482 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1483 p->NextEntryOffset = 0;
1485 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1486 p->EaValueLength = sizeof (fattr3);
1487 strcpy (p->EaName, NFS_V3_ATTR);
1488 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1489 memset (nfs_attr, 0, sizeof (fattr3));
1490 nfs_attr->type = NF3DIR;
1491 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1493 status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
1494 pc.get_object_attr (attr, sa), &io, NULL,
1495 FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
1497 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1498 | FILE_OPEN_FOR_BACKUP_INTENT,
1500 if (NT_SUCCESS (status))
1502 if (has_acls () && pc.isremote ())
1503 set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
1504 S_IFDIR | ((mode & 07777) & ~cygheap->umask));
1509 __seterrno_from_nt_status (status);
1515 fhandler_disk_file::rmdir ()
1517 extern NTSTATUS unlink_nt (path_conv &pc);
1521 set_errno (ENOTDIR);
1530 NTSTATUS status = unlink_nt (pc);
1532 /* Check for existence of remote dirs after trying to delete them.
1534 - Sometimes SMB indicates failure when it really succeeds.
1535 - Removeing a directory on a samba drive doesn't return an error if the
1536 directory can't be removed because it's not empty. */
1539 OBJECT_ATTRIBUTES attr;
1540 FILE_BASIC_INFORMATION fbi;
1542 if (NT_SUCCESS (NtQueryAttributesFile
1543 (pc.get_object_attr (attr, sec_none_nih), &fbi)))
1544 status = STATUS_DIRECTORY_NOT_EMPTY;
1546 status = STATUS_SUCCESS;
1548 if (!NT_SUCCESS (status))
1550 __seterrno_from_nt_status (status);
1556 /* This is the minimal number of entries which fit into the readdir cache.
1557 The number of bytes allocated by the cache is determined by this number,
1558 To tune caching, just tweak this number. To get a feeling for the size,
1559 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1561 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1563 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1564 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1565 + (NAME_MAX + 1) * sizeof (WCHAR)))
1569 char __cache[DIR_BUF_SIZE]; /* W2K needs this buffer 8 byte aligned. */
1573 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1574 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1576 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1579 fhandler_disk_file::opendir (int fd)
1585 set_errno (ENOTDIR);
1586 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1588 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1594 else if ((dir->__d_dirent =
1595 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1603 if (cfd < 0 && fd < 0)
1606 dir->__d_dirent->__d_version = __DIRENT_VERSION;
1607 dir->__d_cookie = __DIRENT_COOKIE;
1608 dir->__handle = INVALID_HANDLE_VALUE;
1609 dir->__d_position = 0;
1610 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1611 ? dirent_isroot : 0;
1612 dir->__d_internal = (unsigned) new __DIR_mounts (get_name ());
1613 d_cachepos (dir) = 0;
1615 if (!pc.iscygdrive ())
1619 /* opendir() case. Initialize with given directory name and
1620 NULL directory handle. */
1621 OBJECT_ATTRIBUTES attr;
1625 status = NtOpenFile (&get_handle (),
1626 SYNCHRONIZE | FILE_LIST_DIRECTORY,
1627 pc.get_object_attr (attr, sec_none_nih),
1628 &io, FILE_SHARE_VALID_FLAGS,
1629 FILE_SYNCHRONOUS_IO_NONALERT
1630 | FILE_OPEN_FOR_BACKUP_INTENT
1631 | FILE_DIRECTORY_FILE);
1632 if (!NT_SUCCESS (status))
1634 __seterrno_from_nt_status (status);
1639 /* FileIdBothDirectoryInformation is apparently unsupported on
1640 XP when accessing directories on UDF. When trying to use it
1641 so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1642 It's not clear if the call isn't also unsupported on other
1643 OS/FS combinations (say, Win2K/CDFS or so). Instead of
1644 testing in readdir for yet another error code, let's use
1645 FileIdBothDirectoryInformation only on filesystems supporting
1646 persistent ACLs, FileDirectoryInformation otherwise.
1648 NFS clients hide dangling symlinks from directory queries,
1649 unless you use the FileNamesInformation info class.
1650 On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
1651 works fine, but only if the NFS share is mounted to a drive
1652 letter. TODO: We don't test that here for now, but it might
1653 be worth to test if there's a speed gain in using
1654 FileIdBothDirectoryInformation, because it doesn't require to
1655 open the file to read the inode number. */
1656 if (pc.hasgood_inode ())
1658 dir->__flags |= dirent_set_d_ino;
1659 if (pc.fs_is_nfs ())
1660 dir->__flags |= dirent_nfs_d_ino;
1661 else if (wincap.has_fileid_dirinfo ()
1662 && !pc.has_buggy_fileid_dirinfo ())
1663 dir->__flags |= dirent_get_d_ino;
1670 /* Filling cfd with `this' (aka storing this in the file
1671 descriptor table should only happen after it's clear that
1672 opendir doesn't fail, otherwise we end up cfree'ing the
1673 fhandler twice, once in opendir() in dir.cc, the second
1674 time on exit. Nasty, nasty... */
1677 if (pc.iscygdrive ())
1678 cfd->nohandle (true);
1680 set_close_on_exec (true);
1685 syscall_printf ("%p = opendir (%s)", res, get_name ());
1689 delete d_mounts (dir);
1691 free (dir->__d_dirent);
1693 free (dir->__d_dirname);
1700 readdir_get_ino (const char *path, bool dot_dot)
1705 OBJECT_ATTRIBUTES attr;
1711 fname = (char *) alloca (strlen (path) + 4);
1712 char *c = stpcpy (fname, path);
1718 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN);
1719 if (pc.isspecial ())
1721 if (!stat_worker (pc, &st))
1724 else if (!pc.hasgood_inode ())
1725 ino = hash_path_name (0, pc.get_nt_native_path ());
1726 else if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1727 pc.get_object_attr (attr, sec_none_nih),
1728 &io, FILE_SHARE_VALID_FLAGS,
1729 FILE_OPEN_FOR_BACKUP_INTENT
1730 | (pc.is_rep_symlink ()
1731 ? FILE_OPEN_REPARSE_POINT : 0))))
1733 ino = get_ino_by_handle (pc, hdl);
1735 ino = hash_path_name (0, pc.get_nt_native_path ());
1742 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1743 DWORD attr, PUNICODE_STRING fname)
1748 if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
1751 return geterrno_from_win_error (w32_err);
1754 dir->__flags &= ~dirent_set_d_ino;
1757 /* Set d_type if type can be determined from file attributes.
1758 FILE_ATTRIBUTE_SYSTEM ommitted to leave DT_UNKNOWN for old symlinks.
1759 For new symlinks, d_type will be reset to DT_UNKNOWN below. */
1761 !(attr & ( ~FILE_ATTRIBUTE_VALID_FLAGS
1762 | FILE_ATTRIBUTE_SYSTEM
1763 | FILE_ATTRIBUTE_REPARSE_POINT)))
1765 if (attr & FILE_ATTRIBUTE_DIRECTORY)
1766 de->d_type = DT_DIR;
1768 de->d_type = DT_REG;
1771 /* Check for directory reparse point. These are potential volume mount
1772 points which have another inode than the underlying directory. */
1773 if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1774 == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1777 OBJECT_ATTRIBUTES attr;
1780 InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
1781 get_handle (), NULL);
1782 if (is_volume_mountpoint (&attr)
1783 && (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
1784 FILE_SHARE_VALID_FLAGS,
1785 FILE_OPEN_FOR_BACKUP_INTENT))))
1787 de->d_ino = get_ino_by_handle (pc, reph);
1792 /* Check for Windows shortcut. If it's a Cygwin or U/WIN
1793 symlink, drop the .lnk suffix. */
1794 if ((attr & FILE_ATTRIBUTE_READONLY) && fname->Length > 4 * sizeof (WCHAR))
1796 UNICODE_STRING uname;
1798 RtlInitCountedUnicodeString (&uname,
1800 + fname->Length / sizeof (WCHAR) - 4,
1801 4 * sizeof (WCHAR));
1802 if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
1805 UNICODE_STRING fbuf;
1808 RtlCopyUnicodeString (&fbuf, pc.get_nt_native_path ());
1809 RtlAppendUnicodeToString (&fbuf, L"\\");
1810 RtlAppendUnicodeStringToString (&fbuf, fname);
1811 fbuf.Buffer += 4; /* Skip leading \??\ */
1812 fbuf.Length -= 4 * sizeof (WCHAR);
1813 if (fbuf.Buffer[1] != L':') /* UNC path */
1815 *(fbuf.Buffer += 2) = L'\\';
1816 fbuf.Length -= 2 * sizeof (WCHAR);
1818 path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
1819 if (fpath.issymlink () || fpath.is_fs_special ())
1821 fname->Length -= 4 * sizeof (WCHAR);
1822 de->d_type = DT_UNKNOWN;
1827 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
1828 fname->Length / sizeof (WCHAR));
1830 /* Don't try to optimize relative to dir->__d_position. On several
1831 filesystems it's no safe bet that "." and ".." entries always
1833 if (de->d_name[0] == '.')
1835 if (de->d_name[1] == '\0')
1836 dir->__flags |= dirent_saw_dot;
1837 else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
1838 dir->__flags |= dirent_saw_dot_dot;
1844 fhandler_disk_file::readdir (DIR *dir, dirent *de)
1847 NTSTATUS status = STATUS_SUCCESS;
1848 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
1850 ULONG FileNameLength;
1851 ULONG FileAttributes = 0;
1853 UNICODE_STRING fname;
1855 /* d_cachepos always refers to the next cache entry to use. If it's 0
1856 we must reload the cache. */
1857 if (d_cachepos (dir) == 0)
1859 if ((dir->__flags & dirent_get_d_ino))
1861 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1862 d_cache (dir), DIR_BUF_SIZE,
1863 FileIdBothDirectoryInformation,
1864 FALSE, NULL, dir->__d_position == 0);
1865 /* FileIdBothDirectoryInformation isn't supported for remote drives
1866 on NT4 and 2K systems, and it's also not supported on 2K at all,
1867 when accessing network drives on any remote OS. We just fall
1868 back to using a standard directory query in this case and note
1869 this case using the dirent_get_d_ino flag. */
1870 if (status == STATUS_INVALID_LEVEL
1871 || status == STATUS_INVALID_PARAMETER
1872 || status == STATUS_INVALID_INFO_CLASS)
1873 dir->__flags &= ~dirent_get_d_ino;
1874 /* Something weird happens on Samba up to version 3.0.21c, which is
1875 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
1876 nicely, but only up to the 128th entry in the directory. After
1877 reaching this entry, the next call to NtQueryDirectoryFile
1878 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
1879 Why should we care, we can just switch to FileDirectoryInformation,
1880 isn't it? Nope! The next call to
1881 NtQueryDirectoryFile(FileDirectoryInformation)
1882 actually returns STATUS_NO_MORE_FILES, regardless how many files
1883 are left unread in the directory. This does not happen when using
1884 FileDirectoryInformation right from the start, but since
1885 we can't decide whether the server we're talking with has this
1886 bug or not, we end up serving Samba shares always in the slow
1887 mode using FileDirectoryInformation. So, what we do here is
1888 to implement the solution suggested by Andrew Tridgell, we just
1889 reread all entries up to dir->d_position using
1890 FileDirectoryInformation.
1891 However, We do *not* mark this server as broken and fall back to
1892 using FileDirectoryInformation further on. This would slow
1893 down every access to such a server, even for directories under
1894 128 entries. Also, bigger dirs only suffer from one additional
1895 call per full directory scan, which shouldn't be too big a hit.
1896 This can easily be changed if necessary. */
1897 if (status == STATUS_INVALID_LEVEL && dir->__d_position)
1899 d_cachepos (dir) = 0;
1900 for (int cnt = 0; cnt < dir->__d_position; ++cnt)
1902 if (d_cachepos (dir) == 0)
1904 status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
1905 NULL, &io, d_cache (dir),
1907 FileDirectoryInformation,
1908 FALSE, NULL, cnt == 0);
1909 if (!NT_SUCCESS (status))
1912 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
1913 + d_cachepos (dir));
1914 if (buf->NextEntryOffset == 0)
1915 d_cachepos (dir) = 0;
1917 d_cachepos (dir) += buf->NextEntryOffset;
1922 if (!(dir->__flags & dirent_get_d_ino))
1923 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1924 d_cache (dir), DIR_BUF_SIZE,
1925 (dir->__flags & dirent_nfs_d_ino)
1926 ? FileNamesInformation
1927 : FileDirectoryInformation,
1928 FALSE, NULL, dir->__d_position == 0);
1933 if (status == STATUS_NO_MORE_FILES)
1935 else if (!NT_SUCCESS (status))
1936 debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
1937 status, RtlNtStatusToDosError (status));
1940 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
1941 if (buf->NextEntryOffset == 0)
1942 d_cachepos (dir) = 0;
1944 d_cachepos (dir) += buf->NextEntryOffset;
1945 if ((dir->__flags & dirent_get_d_ino))
1947 FileName = buf->FileName;
1948 FileNameLength = buf->FileNameLength;
1949 FileAttributes = buf->FileAttributes;
1950 if ((dir->__flags & dirent_set_d_ino))
1951 de->d_ino = buf->FileId.QuadPart;
1953 else if ((dir->__flags & dirent_nfs_d_ino))
1955 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
1956 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
1960 FileName = ((PFILE_DIRECTORY_INFORMATION) buf)->FileName;
1961 FileNameLength = ((PFILE_DIRECTORY_INFORMATION) buf)->FileNameLength;
1962 FileAttributes = ((PFILE_DIRECTORY_INFORMATION) buf)->FileAttributes;
1964 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
1965 de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
1966 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
1968 /* Don't try to optimize relative to dir->__d_position. On several
1969 filesystems it's no safe bet that "." and ".." entries always
1971 if (FileNameLength == sizeof (WCHAR) && FileName[0] == '.')
1972 de->d_ino = get_ino_by_handle (pc, get_handle ());
1973 else if (FileNameLength == 2 * sizeof (WCHAR)
1974 && FileName[0] == L'.' && FileName[1] == L'.')
1976 if (!(dir->__flags & dirent_isroot))
1977 de->d_ino = readdir_get_ino (get_name (), true);
1979 de->d_ino = get_ino_by_handle (pc, get_handle ());
1983 OBJECT_ATTRIBUTES attr;
1987 InitializeObjectAttributes (&attr, &fname,
1988 pc.objcaseinsensitive (),
1989 get_handle (), NULL);
1990 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
1991 NtOpenFile here returns the inode number of the symlink target,
1992 rather than the inode number of the symlink itself.
1994 Worse, trying to open a symlink without setting the special
1995 "ActOnSymlink" EA triggers a bug in Windows 7 which results
1996 in a timeout of up to 20 seconds, followed by two exceptions
1999 Since both results are far from desirable, we open symlinks
2000 on NFS so that we get the right inode and a happy W7.
2001 And, since some filesystems choke on the EAs, we don't
2002 use them unconditionally. */
2003 f_status = (dir->__flags & dirent_nfs_d_ino)
2004 ? NtCreateFile (&hdl, READ_CONTROL, &attr, &io,
2005 NULL, 0, FILE_SHARE_VALID_FLAGS,
2006 FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT,
2007 &nfs_aol_ffei, sizeof nfs_aol_ffei)
2008 : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
2009 FILE_SHARE_VALID_FLAGS,
2010 FILE_OPEN_FOR_BACKUP_INTENT
2011 | FILE_OPEN_REPARSE_POINT);
2012 if (NT_SUCCESS (f_status))
2014 de->d_ino = get_ino_by_handle (pc, hdl);
2018 /* Untrusted file system. Don't try to fetch inode number again. */
2020 dir->__flags &= ~dirent_set_d_ino;
2024 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
2025 buf ? FileAttributes : 0, &fname)))
2026 dir->__d_position++;
2027 else if (!(dir->__flags & dirent_saw_dot))
2029 strcpy (de->d_name , ".");
2030 de->d_ino = get_ino_by_handle (pc, get_handle ());
2031 dir->__d_position++;
2032 dir->__flags |= dirent_saw_dot;
2035 else if (!(dir->__flags & dirent_saw_dot_dot))
2037 strcpy (de->d_name , "..");
2038 if (!(dir->__flags & dirent_isroot))
2039 de->d_ino = readdir_get_ino (get_name (), true);
2041 de->d_ino = get_ino_by_handle (pc, get_handle ());
2042 dir->__d_position++;
2043 dir->__flags |= dirent_saw_dot_dot;
2047 syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, &de, res ? "***" : de->d_name);
2052 fhandler_disk_file::telldir (DIR *dir)
2054 return dir->__d_position;
2058 fhandler_disk_file::seekdir (DIR *dir, _off64_t loc)
2061 while (loc > dir->__d_position)
2062 if (!::readdir (dir))
2067 fhandler_disk_file::rewinddir (DIR *dir)
2069 d_cachepos (dir) = 0;
2070 if (wincap.has_buggy_restart_scan () && isremote ())
2072 /* This works around a W2K bug. The RestartScan parameter in calls
2073 to NtQueryDirectoryFile on remote shares is ignored, thus
2074 resulting in not being able to rewind on remote shares. By
2075 reopening the directory, we get a fresh new directory pointer. */
2076 OBJECT_ATTRIBUTES attr;
2081 InitializeObjectAttributes (&attr, &ro_u_empty, pc.objcaseinsensitive (),
2082 get_handle (), NULL);
2083 status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2084 &attr, &io, FILE_SHARE_VALID_FLAGS,
2085 FILE_SYNCHRONOUS_IO_NONALERT
2086 | FILE_OPEN_FOR_BACKUP_INTENT
2087 | FILE_DIRECTORY_FILE);
2088 if (!NT_SUCCESS (stat))
2089 debug_printf ("Unable to reopen dir %s, NT error: %p",
2090 get_name (), status);
2093 NtClose (get_handle ());
2094 set_io_handle (new_dir);
2097 dir->__d_position = 0;
2098 d_mounts (dir)->rewind ();
2102 fhandler_disk_file::closedir (DIR *dir)
2107 delete d_mounts (dir);
2110 else if (get_handle () == INVALID_HANDLE_VALUE)
2115 else if (!NT_SUCCESS (status = NtClose (get_handle ())))
2117 __seterrno_from_nt_status (status);
2120 syscall_printf ("%d = closedir (%p, %s)", res, dir, get_name ());
2124 fhandler_cygdrive::fhandler_cygdrive () :
2125 fhandler_disk_file (), ndrives (0), pdrive (NULL)
2130 fhandler_cygdrive::open (int flags, mode_t mode)
2132 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2137 if (flags & O_WRONLY)
2149 fhandler_cygdrive::close ()
2155 fhandler_cygdrive::set_drives ()
2157 pdrive = pdrive_buf;
2158 ndrives = GetLogicalDriveStrings (sizeof pdrive_buf, pdrive_buf) / DRVSZ;
2162 fhandler_cygdrive::fstat (struct __stat64 *buf)
2164 fhandler_base::fstat (buf);
2166 buf->st_mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
2169 char flptst[] = "X:";
2171 for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
2172 if (is_floppy ((flptst[0] = *p, flptst))
2173 || GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
2175 buf->st_nlink = n + 2;
2180 fhandler_cygdrive::opendir (int fd)
2184 dir = fhandler_disk_file::opendir (fd);
2185 if (dir && !ndrives)
2192 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2194 char flptst[] = "X:";
2198 if (!pdrive || !*pdrive)
2200 if (!(dir->__flags & dirent_saw_dot))
2202 de->d_name[0] = '.';
2203 de->d_name[1] = '\0';
2208 if (!is_floppy ((flptst[0] = *pdrive, flptst))
2209 && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2211 pdrive = strchr (pdrive, '\0') + 1;
2213 *de->d_name = cyg_tolower (*pdrive);
2214 de->d_name[1] = '\0';
2215 user_shared->warned_msdos = true;
2216 de->d_ino = readdir_get_ino (pdrive, false);
2217 dir->__d_position++;
2218 pdrive = strchr (pdrive, '\0') + 1;
2219 syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2224 fhandler_cygdrive::rewinddir (DIR *dir)
2226 pdrive = pdrive_buf;
2227 dir->__d_position = 0;
2231 fhandler_cygdrive::closedir (DIR *dir)
2233 pdrive = pdrive_buf;