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 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
1192 set_errno (newpc.error);
1196 if (newpc.exists ())
1198 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1203 char new_buf[strlen (newpath) + 5];
1206 if (pc.is_lnk_special ())
1208 /* Shortcut hack. */
1209 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1211 newpc.check (newpath, PC_SYM_NOFOLLOW);
1213 else if (!pc.isdir ()
1215 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1217 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1220 /* Executable hack. */
1221 stpcpy (stpcpy (new_buf, newpath), ".exe");
1223 newpc.check (newpath, PC_SYM_NOFOLLOW);
1229 OBJECT_ATTRIBUTES attr;
1231 status = NtOpenFile (&fh, READ_CONTROL,
1232 pc.get_object_attr (attr, sec_none_nih), &io,
1233 FILE_SHARE_VALID_FLAGS,
1234 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
1235 if (!NT_SUCCESS (status))
1237 __seterrno_from_nt_status (status);
1240 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1241 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1242 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1243 pfli->ReplaceIfExists = FALSE;
1244 pfli->RootDirectory = NULL;
1245 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1246 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1248 if (!NT_SUCCESS (status))
1250 if (status == STATUS_INVALID_DEVICE_REQUEST)
1252 /* FS doesn't support hard links. Try to copy file. */
1253 WCHAR pcw[(pc.get_nt_native_path ()->Length / sizeof (WCHAR)) + 1];
1254 WCHAR newpcw[(newpc.get_nt_native_path ()->Length / sizeof (WCHAR))
1256 if (!CopyFileW (pc.get_wide_win32_path (pcw),
1257 newpc.get_wide_win32_path (newpcw), TRUE))
1262 SetFileAttributesW (newpcw, pc.file_attributes ());
1266 __seterrno_from_nt_status (status);
1274 fhandler_disk_file::utimens (const struct timespec *tvp)
1276 return utimens_fs (tvp);
1280 fhandler_base::utimens_fs (const struct timespec *tvp)
1282 struct timespec timeofday;
1283 struct timespec tmp[2];
1284 bool closeit = false;
1288 query_open (query_write_attributes);
1289 if (!open_fs (O_BINARY, 0))
1291 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1292 to change the timestamps. Unfortunately it's not sufficient for a
1293 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1294 for writing, though this fails for R/O files of course. */
1295 query_open (no_query);
1296 if (!open_fs (O_WRONLY | O_BINARY, 0))
1298 syscall_printf ("Opening file failed");
1305 gettimeofday (reinterpret_cast<struct timeval *> (&timeofday), 0);
1306 timeofday.tv_nsec *= 1000;
1308 tmp[1] = tmp[0] = timeofday;
1311 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1312 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1317 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1318 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1320 debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
1323 FILE_BASIC_INFORMATION fbi;
1325 fbi.CreationTime.QuadPart = 0LL;
1326 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1327 timespec_to_filetime (&tmp[0], (LPFILETIME) &fbi.LastAccessTime);
1328 timespec_to_filetime (&tmp[1], (LPFILETIME) &fbi.LastWriteTime);
1329 fbi.ChangeTime.QuadPart = 0LL;
1330 fbi.FileAttributes = 0;
1331 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1332 FileBasicInformation);
1333 /* For this special case for MVFS see the comment in
1334 fhandler_disk_file::fchmod. */
1335 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
1337 OBJECT_ATTRIBUTES attr;
1340 InitializeObjectAttributes (&attr, &ro_u_empty, 0, get_handle (), NULL);
1341 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
1342 FILE_SHARE_VALID_FLAGS,
1343 FILE_OPEN_FOR_BACKUP_INTENT)))
1345 NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1346 FileBasicInformation);
1352 /* Opening a directory on a 9x share from a NT machine works(!), but
1353 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1354 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1356 __seterrno_from_nt_status (status);
1362 fhandler_disk_file::fhandler_disk_file () :
1367 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1374 fhandler_disk_file::open (int flags, mode_t mode)
1376 return open_fs (flags, mode);
1380 fhandler_base::open_fs (int flags, mode_t mode)
1382 /* Unfortunately NT allows to open directories for writing, but that's
1383 disallowed according to SUSv3. */
1384 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1390 int res = fhandler_base::open (flags | O_DIROPEN, mode);
1394 /* This is for file systems known for having a buggy CreateFile call
1395 which might return a valid HANDLE without having actually opened
1397 The only known file system to date is the SUN NFS Solstice Client 3.1
1398 which returns a valid handle when trying to open a file in a nonexistent
1400 if (pc.has_buggy_open () && !pc.exists ())
1402 debug_printf ("Buggy open detected.");
1408 ino = get_ino_by_handle (pc, get_handle ());
1409 /* A unique ID is necessary to recognize fhandler entries which are
1410 duplicated by dup(2) or fork(2). */
1411 AllocateLocallyUniqueId ((PLUID) &unique_id);
1414 syscall_printf ("%d = fhandler_disk_file::open (%S, %p)", res,
1415 pc.get_nt_native_path (), flags);
1420 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
1423 _off64_t curpos = lseek (0, SEEK_CUR);
1424 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1428 size_t tmp_count = count;
1429 read (buf, tmp_count);
1430 if (lseek (curpos, SEEK_SET) >= 0)
1431 res = (ssize_t) tmp_count;
1435 debug_printf ("%d = pread (%p, %d, %d)\n", res, buf, count, offset);
1440 fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
1443 _off64_t curpos = lseek (0, SEEK_CUR);
1444 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1448 res = (ssize_t) write (buf, count);
1449 if (lseek (curpos, SEEK_SET) < 0)
1452 debug_printf ("%d = pwrite (%p, %d, %d)\n", res, buf, count, offset);
1457 fhandler_disk_file::mkdir (mode_t mode)
1460 SECURITY_ATTRIBUTES sa = sec_none_nih;
1461 security_descriptor sd;
1463 /* See comments in fhander_base::open () for an explanation why we defer
1464 setting security attributes on remote files. */
1465 if (has_acls () && !pc.isremote ())
1466 set_security_attribute (pc, S_IFDIR | ((mode & 07777) & ~cygheap->umask),
1471 OBJECT_ATTRIBUTES attr;
1473 PFILE_FULL_EA_INFORMATION p = NULL;
1476 if (pc.fs_is_nfs ())
1478 /* When creating a dir on an NFS share, we have to set the
1479 file mode by writing a NFS fattr3 structure with the
1480 correct mode bits set. */
1481 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1483 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1484 p->NextEntryOffset = 0;
1486 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1487 p->EaValueLength = sizeof (fattr3);
1488 strcpy (p->EaName, NFS_V3_ATTR);
1489 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1490 memset (nfs_attr, 0, sizeof (fattr3));
1491 nfs_attr->type = NF3DIR;
1492 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1494 status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
1495 pc.get_object_attr (attr, sa), &io, NULL,
1496 FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
1498 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1499 | FILE_OPEN_FOR_BACKUP_INTENT,
1501 if (NT_SUCCESS (status))
1503 if (has_acls () && pc.isremote ())
1504 set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
1505 S_IFDIR | ((mode & 07777) & ~cygheap->umask));
1510 __seterrno_from_nt_status (status);
1516 fhandler_disk_file::rmdir ()
1518 extern NTSTATUS unlink_nt (path_conv &pc);
1522 set_errno (ENOTDIR);
1531 NTSTATUS status = unlink_nt (pc);
1533 /* Check for existence of remote dirs after trying to delete them.
1535 - Sometimes SMB indicates failure when it really succeeds.
1536 - Removeing a directory on a samba drive doesn't return an error if the
1537 directory can't be removed because it's not empty. */
1540 OBJECT_ATTRIBUTES attr;
1541 FILE_BASIC_INFORMATION fbi;
1543 if (NT_SUCCESS (NtQueryAttributesFile
1544 (pc.get_object_attr (attr, sec_none_nih), &fbi)))
1545 status = STATUS_DIRECTORY_NOT_EMPTY;
1547 status = STATUS_SUCCESS;
1549 if (!NT_SUCCESS (status))
1551 __seterrno_from_nt_status (status);
1557 /* This is the minimal number of entries which fit into the readdir cache.
1558 The number of bytes allocated by the cache is determined by this number,
1559 To tune caching, just tweak this number. To get a feeling for the size,
1560 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1562 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1564 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1565 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1566 + (NAME_MAX + 1) * sizeof (WCHAR)))
1570 char __cache[DIR_BUF_SIZE]; /* W2K needs this buffer 8 byte aligned. */
1574 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1575 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1577 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1580 fhandler_disk_file::opendir (int fd)
1586 set_errno (ENOTDIR);
1587 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1589 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1595 else if ((dir->__d_dirent =
1596 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1604 if (cfd < 0 && fd < 0)
1607 dir->__d_dirent->__d_version = __DIRENT_VERSION;
1608 dir->__d_cookie = __DIRENT_COOKIE;
1609 dir->__handle = INVALID_HANDLE_VALUE;
1610 dir->__d_position = 0;
1611 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1612 ? dirent_isroot : 0;
1613 dir->__d_internal = (unsigned) new __DIR_mounts (get_name ());
1614 d_cachepos (dir) = 0;
1616 if (!pc.iscygdrive ())
1620 /* opendir() case. Initialize with given directory name and
1621 NULL directory handle. */
1622 OBJECT_ATTRIBUTES attr;
1626 status = NtOpenFile (&get_handle (),
1627 SYNCHRONIZE | FILE_LIST_DIRECTORY,
1628 pc.get_object_attr (attr, sec_none_nih),
1629 &io, FILE_SHARE_VALID_FLAGS,
1630 FILE_SYNCHRONOUS_IO_NONALERT
1631 | FILE_OPEN_FOR_BACKUP_INTENT
1632 | FILE_DIRECTORY_FILE);
1633 if (!NT_SUCCESS (status))
1635 __seterrno_from_nt_status (status);
1640 /* FileIdBothDirectoryInformation is apparently unsupported on
1641 XP when accessing directories on UDF. When trying to use it
1642 so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1643 It's not clear if the call isn't also unsupported on other
1644 OS/FS combinations (say, Win2K/CDFS or so). Instead of
1645 testing in readdir for yet another error code, let's use
1646 FileIdBothDirectoryInformation only on filesystems supporting
1647 persistent ACLs, FileDirectoryInformation otherwise.
1649 NFS clients hide dangling symlinks from directory queries,
1650 unless you use the FileNamesInformation info class.
1651 On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
1652 works fine, but only if the NFS share is mounted to a drive
1653 letter. TODO: We don't test that here for now, but it might
1654 be worth to test if there's a speed gain in using
1655 FileIdBothDirectoryInformation, because it doesn't require to
1656 open the file to read the inode number. */
1657 if (pc.hasgood_inode ())
1659 dir->__flags |= dirent_set_d_ino;
1660 if (pc.fs_is_nfs ())
1661 dir->__flags |= dirent_nfs_d_ino;
1662 else if (wincap.has_fileid_dirinfo ()
1663 && !pc.has_buggy_fileid_dirinfo ())
1664 dir->__flags |= dirent_get_d_ino;
1671 /* Filling cfd with `this' (aka storing this in the file
1672 descriptor table should only happen after it's clear that
1673 opendir doesn't fail, otherwise we end up cfree'ing the
1674 fhandler twice, once in opendir() in dir.cc, the second
1675 time on exit. Nasty, nasty... */
1678 if (pc.iscygdrive ())
1679 cfd->nohandle (true);
1681 set_close_on_exec (true);
1686 syscall_printf ("%p = opendir (%s)", res, get_name ());
1690 delete d_mounts (dir);
1692 free (dir->__d_dirent);
1694 free (dir->__d_dirname);
1701 readdir_get_ino (const char *path, bool dot_dot)
1706 OBJECT_ATTRIBUTES attr;
1712 fname = (char *) alloca (strlen (path) + 4);
1713 char *c = stpcpy (fname, path);
1719 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN);
1720 if (pc.isspecial ())
1722 if (!stat_worker (pc, &st))
1725 else if (!pc.hasgood_inode ())
1726 ino = hash_path_name (0, pc.get_nt_native_path ());
1727 else if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1728 pc.get_object_attr (attr, sec_none_nih),
1729 &io, FILE_SHARE_VALID_FLAGS,
1730 FILE_OPEN_FOR_BACKUP_INTENT
1731 | (pc.is_rep_symlink ()
1732 ? FILE_OPEN_REPARSE_POINT : 0))))
1734 ino = get_ino_by_handle (pc, hdl);
1736 ino = hash_path_name (0, pc.get_nt_native_path ());
1743 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1744 DWORD attr, PUNICODE_STRING fname)
1749 if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
1752 return geterrno_from_win_error (w32_err);
1755 dir->__flags &= ~dirent_set_d_ino;
1758 /* Set d_type if type can be determined from file attributes.
1759 FILE_ATTRIBUTE_SYSTEM ommitted to leave DT_UNKNOWN for old symlinks.
1760 For new symlinks, d_type will be reset to DT_UNKNOWN below. */
1762 !(attr & ( ~FILE_ATTRIBUTE_VALID_FLAGS
1763 | FILE_ATTRIBUTE_SYSTEM
1764 | FILE_ATTRIBUTE_REPARSE_POINT)))
1766 if (attr & FILE_ATTRIBUTE_DIRECTORY)
1767 de->d_type = DT_DIR;
1769 de->d_type = DT_REG;
1772 /* Check for directory reparse point. These are potential volume mount
1773 points which have another inode than the underlying directory. */
1774 if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1775 == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1778 OBJECT_ATTRIBUTES attr;
1781 InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
1782 get_handle (), NULL);
1783 if (is_volume_mountpoint (&attr)
1784 && (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
1785 FILE_SHARE_VALID_FLAGS,
1786 FILE_OPEN_FOR_BACKUP_INTENT))))
1788 de->d_ino = get_ino_by_handle (pc, reph);
1793 /* Check for Windows shortcut. If it's a Cygwin or U/WIN
1794 symlink, drop the .lnk suffix. */
1795 if ((attr & FILE_ATTRIBUTE_READONLY) && fname->Length > 4 * sizeof (WCHAR))
1797 UNICODE_STRING uname;
1799 RtlInitCountedUnicodeString (&uname,
1801 + fname->Length / sizeof (WCHAR) - 4,
1802 4 * sizeof (WCHAR));
1803 if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
1806 UNICODE_STRING fbuf;
1809 RtlCopyUnicodeString (&fbuf, pc.get_nt_native_path ());
1810 RtlAppendUnicodeToString (&fbuf, L"\\");
1811 RtlAppendUnicodeStringToString (&fbuf, fname);
1812 fbuf.Buffer += 4; /* Skip leading \??\ */
1813 fbuf.Length -= 4 * sizeof (WCHAR);
1814 if (fbuf.Buffer[1] != L':') /* UNC path */
1816 *(fbuf.Buffer += 2) = L'\\';
1817 fbuf.Length -= 2 * sizeof (WCHAR);
1819 path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
1820 if (fpath.issymlink () || fpath.is_fs_special ())
1822 fname->Length -= 4 * sizeof (WCHAR);
1823 de->d_type = DT_UNKNOWN;
1828 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
1829 fname->Length / sizeof (WCHAR));
1831 if (dir->__d_position == 0 && !strcmp (de->d_name, "."))
1832 dir->__flags |= dirent_saw_dot;
1833 else if (dir->__d_position == 1 && !strcmp (de->d_name, ".."))
1834 dir->__flags |= dirent_saw_dot_dot;
1839 fhandler_disk_file::readdir (DIR *dir, dirent *de)
1842 NTSTATUS status = STATUS_SUCCESS;
1843 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
1845 ULONG FileNameLength;
1846 ULONG FileAttributes = 0;
1848 UNICODE_STRING fname;
1850 /* d_cachepos always refers to the next cache entry to use. If it's 0
1851 we must reload the cache. */
1852 if (d_cachepos (dir) == 0)
1854 if ((dir->__flags & dirent_get_d_ino))
1856 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1857 d_cache (dir), DIR_BUF_SIZE,
1858 FileIdBothDirectoryInformation,
1859 FALSE, NULL, dir->__d_position == 0);
1860 /* FileIdBothDirectoryInformation isn't supported for remote drives
1861 on NT4 and 2K systems, and it's also not supported on 2K at all,
1862 when accessing network drives on any remote OS. We just fall
1863 back to using a standard directory query in this case and note
1864 this case using the dirent_get_d_ino flag. */
1865 if (status == STATUS_INVALID_LEVEL
1866 || status == STATUS_INVALID_PARAMETER
1867 || status == STATUS_INVALID_INFO_CLASS)
1868 dir->__flags &= ~dirent_get_d_ino;
1869 /* Something weird happens on Samba up to version 3.0.21c, which is
1870 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
1871 nicely, but only up to the 128th entry in the directory. After
1872 reaching this entry, the next call to NtQueryDirectoryFile
1873 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
1874 Why should we care, we can just switch to FileDirectoryInformation,
1875 isn't it? Nope! The next call to
1876 NtQueryDirectoryFile(FileDirectoryInformation)
1877 actually returns STATUS_NO_MORE_FILES, regardless how many files
1878 are left unread in the directory. This does not happen when using
1879 FileDirectoryInformation right from the start, but since
1880 we can't decide whether the server we're talking with has this
1881 bug or not, we end up serving Samba shares always in the slow
1882 mode using FileDirectoryInformation. So, what we do here is
1883 to implement the solution suggested by Andrew Tridgell, we just
1884 reread all entries up to dir->d_position using
1885 FileDirectoryInformation.
1886 However, We do *not* mark this server as broken and fall back to
1887 using FileDirectoryInformation further on. This would slow
1888 down every access to such a server, even for directories under
1889 128 entries. Also, bigger dirs only suffer from one additional
1890 call per full directory scan, which shouldn't be too big a hit.
1891 This can easily be changed if necessary. */
1892 if (status == STATUS_INVALID_LEVEL && dir->__d_position)
1894 d_cachepos (dir) = 0;
1895 for (int cnt = 0; cnt < dir->__d_position; ++cnt)
1897 if (d_cachepos (dir) == 0)
1899 status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
1900 NULL, &io, d_cache (dir),
1902 FileDirectoryInformation,
1903 FALSE, NULL, cnt == 0);
1904 if (!NT_SUCCESS (status))
1907 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
1908 + d_cachepos (dir));
1909 if (buf->NextEntryOffset == 0)
1910 d_cachepos (dir) = 0;
1912 d_cachepos (dir) += buf->NextEntryOffset;
1917 if (!(dir->__flags & dirent_get_d_ino))
1918 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1919 d_cache (dir), DIR_BUF_SIZE,
1920 (dir->__flags & dirent_nfs_d_ino)
1921 ? FileNamesInformation
1922 : FileDirectoryInformation,
1923 FALSE, NULL, dir->__d_position == 0);
1928 if (status == STATUS_NO_MORE_FILES)
1930 else if (!NT_SUCCESS (status))
1931 debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
1932 status, RtlNtStatusToDosError (status));
1935 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
1936 if (buf->NextEntryOffset == 0)
1937 d_cachepos (dir) = 0;
1939 d_cachepos (dir) += buf->NextEntryOffset;
1940 if ((dir->__flags & dirent_get_d_ino))
1942 FileName = buf->FileName;
1943 FileNameLength = buf->FileNameLength;
1944 FileAttributes = buf->FileAttributes;
1945 if ((dir->__flags & dirent_set_d_ino))
1946 de->d_ino = buf->FileId.QuadPart;
1948 else if ((dir->__flags & dirent_nfs_d_ino))
1950 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
1951 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
1955 FileName = ((PFILE_DIRECTORY_INFORMATION) buf)->FileName;
1956 FileNameLength = ((PFILE_DIRECTORY_INFORMATION) buf)->FileNameLength;
1957 FileAttributes = ((PFILE_DIRECTORY_INFORMATION) buf)->FileAttributes;
1959 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
1960 de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
1961 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
1963 OBJECT_ATTRIBUTES attr;
1965 if (dir->__d_position == 0 && FileNameLength == 2
1966 && FileName[0] == '.')
1967 de->d_ino = get_ino_by_handle (pc, get_handle ());
1968 else if (dir->__d_position == 1 && FileNameLength == 4
1969 && FileName[0] == L'.' && FileName[1] == L'.')
1970 if (!(dir->__flags & dirent_isroot))
1971 de->d_ino = readdir_get_ino (get_name (), true);
1973 de->d_ino = get_ino_by_handle (pc, get_handle ());
1978 InitializeObjectAttributes (&attr, &fname,
1979 pc.objcaseinsensitive (),
1980 get_handle (), NULL);
1981 if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
1982 FILE_SHARE_VALID_FLAGS,
1983 FILE_OPEN_FOR_BACKUP_INTENT
1984 | FILE_OPEN_REPARSE_POINT)))
1986 de->d_ino = get_ino_by_handle (pc, hdl);
1990 /* Untrusted file system. Don't try to fetch inode number again. */
1992 dir->__flags &= ~dirent_set_d_ino;
1996 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
1997 buf ? FileAttributes : 0, &fname)))
1998 dir->__d_position++;
1999 else if (!(dir->__flags & dirent_saw_dot))
2001 strcpy (de->d_name , ".");
2002 de->d_ino = get_ino_by_handle (pc, get_handle ());
2003 dir->__d_position++;
2004 dir->__flags |= dirent_saw_dot;
2007 else if (!(dir->__flags & dirent_saw_dot_dot))
2009 strcpy (de->d_name , "..");
2010 if (!(dir->__flags & dirent_isroot))
2011 de->d_ino = readdir_get_ino (get_name (), true);
2013 de->d_ino = get_ino_by_handle (pc, get_handle ());
2014 dir->__d_position++;
2015 dir->__flags |= dirent_saw_dot_dot;
2019 syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, &de, res ? "***" : de->d_name);
2024 fhandler_disk_file::telldir (DIR *dir)
2026 return dir->__d_position;
2030 fhandler_disk_file::seekdir (DIR *dir, _off64_t loc)
2033 while (loc > dir->__d_position)
2034 if (!::readdir (dir))
2039 fhandler_disk_file::rewinddir (DIR *dir)
2041 d_cachepos (dir) = 0;
2042 if (wincap.has_buggy_restart_scan () && isremote ())
2044 /* This works around a W2K bug. The RestartScan parameter in calls
2045 to NtQueryDirectoryFile on remote shares is ignored, thus
2046 resulting in not being able to rewind on remote shares. By
2047 reopening the directory, we get a fresh new directory pointer. */
2048 OBJECT_ATTRIBUTES attr;
2053 InitializeObjectAttributes (&attr, &ro_u_empty, pc.objcaseinsensitive (),
2054 get_handle (), NULL);
2055 status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2056 &attr, &io, FILE_SHARE_VALID_FLAGS,
2057 FILE_SYNCHRONOUS_IO_NONALERT
2058 | FILE_OPEN_FOR_BACKUP_INTENT
2059 | FILE_DIRECTORY_FILE);
2060 if (!NT_SUCCESS (stat))
2061 debug_printf ("Unable to reopen dir %s, NT error: %p",
2062 get_name (), status);
2065 NtClose (get_handle ());
2066 set_io_handle (new_dir);
2069 dir->__d_position = 0;
2070 d_mounts (dir)->rewind ();
2074 fhandler_disk_file::closedir (DIR *dir)
2079 delete d_mounts (dir);
2082 else if (get_handle () == INVALID_HANDLE_VALUE)
2087 else if (!NT_SUCCESS (status = NtClose (get_handle ())))
2089 __seterrno_from_nt_status (status);
2092 syscall_printf ("%d = closedir (%p, %s)", res, dir, get_name ());
2096 fhandler_cygdrive::fhandler_cygdrive () :
2097 fhandler_disk_file (), ndrives (0), pdrive (NULL)
2102 fhandler_cygdrive::open (int flags, mode_t mode)
2104 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2109 if (flags & O_WRONLY)
2121 fhandler_cygdrive::close ()
2127 fhandler_cygdrive::set_drives ()
2129 pdrive = pdrive_buf;
2130 ndrives = GetLogicalDriveStrings (sizeof pdrive_buf, pdrive_buf) / DRVSZ;
2134 fhandler_cygdrive::fstat (struct __stat64 *buf)
2136 fhandler_base::fstat (buf);
2138 buf->st_mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
2141 char flptst[] = "X:";
2143 for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
2144 if (is_floppy ((flptst[0] = *p, flptst))
2145 || GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
2147 buf->st_nlink = n + 2;
2152 fhandler_cygdrive::opendir (int fd)
2156 dir = fhandler_disk_file::opendir (fd);
2157 if (dir && !ndrives)
2164 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2166 char flptst[] = "X:";
2170 if (!pdrive || !*pdrive)
2172 if (!(dir->__flags & dirent_saw_dot))
2174 de->d_name[0] = '.';
2175 de->d_name[1] = '\0';
2180 if (!is_floppy ((flptst[0] = *pdrive, flptst))
2181 && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2183 pdrive = strchr (pdrive, '\0') + 1;
2185 *de->d_name = cyg_tolower (*pdrive);
2186 de->d_name[1] = '\0';
2187 user_shared->warned_msdos = true;
2188 de->d_ino = readdir_get_ino (pdrive, false);
2189 dir->__d_position++;
2190 pdrive = strchr (pdrive, '\0') + 1;
2191 syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2196 fhandler_cygdrive::rewinddir (DIR *dir)
2198 pdrive = pdrive_buf;
2199 dir->__d_position = 0;
2203 fhandler_cygdrive::closedir (DIR *dir)
2205 pdrive = pdrive_buf;