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 /* Correct NTFS security attributes have higher priority */
852 if (!NT_SUCCESS (status))
853 __seterrno_from_nt_status (status);
866 fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
872 /* fake - if not supported, pretend we're like win95
873 where it just works */
874 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
880 query_open (query_write_control);
881 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
889 int res = get_file_attribute (get_handle (), pc, &attrib, &old_uid, NULL);
892 /* Typical Windows default ACLs can contain permissions for one
893 group, while being owned by another user/group. The permission
894 bits returned above are pretty much useless then. Creating a
895 new ACL with these useless permissions results in a potentially
896 broken symlink. So what we do here is to set the underlying
897 permissions of symlinks to a sensible value which allows the
898 world to read the symlink and only the new owner to change it. */
900 attrib = S_IFLNK | STD_RBITS | STD_WBITS;
901 res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
902 /* If you're running a Samba server which has no winbidd running, the
903 uid<->SID mapping is disfunctional. Even trying to chown to your
904 own account fails since the account used on the server is the UNIX
905 account which gets used for the standard user mapping. This is a
906 default mechanism which doesn't know your real Windows SID.
907 There are two possible error codes in different Samba releases for
908 this situation, one of them is unfortunately the not very significant
909 STATUS_ACCESS_DENIED. Instead of relying on the error codes, we're
910 using the below very simple heuristic. If set_file_attribute failed,
911 and the original user account was either already unknown, or one of
912 the standard UNIX accounts, we're faking success. */
913 if (res == -1 && pc.fs_is_samba ())
917 if (old_uid == ILLEGAL_UID
918 || (sid.getfrompw (internal_getpwuid (old_uid))
919 && EqualPrefixSid (sid, well_known_samba_unix_user_fake_sid)))
921 debug_printf ("Faking chown worked on standalone Samba");
933 fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp)
946 /* Open for writing required to be able to set ctime
947 (even though setting the ACL is just pretended). */
949 oret = open (O_WRONLY | O_BINARY, 0);
955 else if (nentries < MIN_ACL_ENTRIES)
961 query_open (query_read_attributes);
962 oret = open (O_BINARY, 0);
964 if ((oret && !fstat_by_handle (&st))
965 || !fstat_by_name (&st))
967 aclbufp[0].a_type = USER_OBJ;
968 aclbufp[0].a_id = st.st_uid;
969 aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
970 aclbufp[1].a_type = GROUP_OBJ;
971 aclbufp[1].a_id = st.st_gid;
972 aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
973 aclbufp[2].a_type = OTHER_OBJ;
974 aclbufp[2].a_id = ILLEGAL_GID;
975 aclbufp[2].a_perm = st.st_mode & S_IRWXO;
976 aclbufp[3].a_type = CLASS_OBJ;
977 aclbufp[3].a_id = ILLEGAL_GID;
978 aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
979 res = MIN_ACL_ENTRIES;
984 res = MIN_ACL_ENTRIES;
995 query_open (cmd == SETACL ? query_write_control : query_read_control);
996 if (!(oret = open (O_BINARY, 0)))
998 if (cmd == GETACL || cmd == GETACLCNT)
999 goto cant_access_acl;
1006 if (!aclsort32 (nentries, 0, aclbufp))
1009 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1013 FILE_BASIC_INFORMATION fbi;
1014 fbi.CreationTime.QuadPart
1015 = fbi.LastAccessTime.QuadPart
1016 = fbi.LastWriteTime.QuadPart
1017 = fbi.ChangeTime.QuadPart = 0LL;
1018 fbi.FileAttributes = (pc.file_attributes ()
1019 & ~FILE_ATTRIBUTE_READONLY)
1020 ?: FILE_ATTRIBUTE_NORMAL;
1021 NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1022 FileBasicInformation);
1030 res = getacl (get_handle (), pc, nentries, aclbufp);
1033 res = getacl (get_handle (), pc, 0, NULL);
1048 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1050 if (pc.is_fs_special ())
1052 set_errno (ENOTSUP);
1055 return read_ea (get_handle (), pc, name, (char *) value, size);
1059 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1062 if (pc.is_fs_special ())
1064 set_errno (ENOTSUP);
1067 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1071 fhandler_disk_file::fadvise (_off64_t offset, _off64_t length, int advice)
1073 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1079 /* Windows only supports advice flags for the whole file. We're using
1080 a simplified test here so that we don't have to ask for the actual
1081 file size. Length == 0 means all bytes starting at offset anyway.
1082 So we only actually follow the advice, if it's given for offset == 0. */
1086 /* We only support normal and sequential mode for now. Everything which
1087 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1088 if (advice != POSIX_FADV_SEQUENTIAL)
1089 advice = POSIX_FADV_NORMAL;
1092 FILE_MODE_INFORMATION fmi;
1093 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1095 FileModeInformation);
1096 if (!NT_SUCCESS (status))
1097 __seterrno_from_nt_status (status);
1100 fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1101 if (advice == POSIX_FADV_SEQUENTIAL)
1102 fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1103 status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1104 FileModeInformation);
1105 if (NT_SUCCESS (status))
1107 __seterrno_from_nt_status (status);
1114 fhandler_disk_file::ftruncate (_off64_t length, bool allow_truncate)
1118 if (length < 0 || !get_handle ())
1120 else if (pc.isdir ())
1122 else if (!(get_access () & GENERIC_WRITE))
1128 FILE_STANDARD_INFORMATION fsi;
1129 FILE_END_OF_FILE_INFORMATION feofi;
1131 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1132 FileStandardInformation);
1133 if (!NT_SUCCESS (status))
1135 __seterrno_from_nt_status (status);
1139 /* If called through posix_fallocate, silently succeed if length
1140 is less than the file's actual length. */
1141 if (!allow_truncate && length < fsi.EndOfFile.QuadPart)
1144 feofi.EndOfFile.QuadPart = length;
1145 /* Create sparse files only when called through ftruncate, not when
1146 called through posix_fallocate. */
1148 && (pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES)
1149 && length >= fsi.EndOfFile.QuadPart + (128 * 1024))
1151 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1152 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1153 syscall_printf ("%p = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1154 status, pc.get_nt_native_path ());
1156 status = NtSetInformationFile (get_handle (), &io,
1157 &feofi, sizeof feofi,
1158 FileEndOfFileInformation);
1159 if (!NT_SUCCESS (status))
1160 __seterrno_from_nt_status (status);
1168 fhandler_disk_file::link (const char *newpath)
1170 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
1173 set_errno (newpc.error);
1177 if (newpc.exists ())
1179 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1184 char new_buf[strlen (newpath) + 5];
1187 if (pc.is_lnk_special ())
1189 /* Shortcut hack. */
1190 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1192 newpc.check (newpath, PC_SYM_NOFOLLOW);
1194 else if (!pc.isdir ()
1196 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1198 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1201 /* Executable hack. */
1202 stpcpy (stpcpy (new_buf, newpath), ".exe");
1204 newpc.check (newpath, PC_SYM_NOFOLLOW);
1210 OBJECT_ATTRIBUTES attr;
1212 status = NtOpenFile (&fh, READ_CONTROL,
1213 pc.get_object_attr (attr, sec_none_nih), &io,
1214 FILE_SHARE_VALID_FLAGS,
1215 FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
1216 if (!NT_SUCCESS (status))
1218 __seterrno_from_nt_status (status);
1221 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1222 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1223 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1224 pfli->ReplaceIfExists = FALSE;
1225 pfli->RootDirectory = NULL;
1226 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1227 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1229 if (!NT_SUCCESS (status))
1231 if (status == STATUS_INVALID_DEVICE_REQUEST)
1233 /* FS doesn't support hard links. Try to copy file. */
1234 WCHAR pcw[(pc.get_nt_native_path ()->Length / sizeof (WCHAR)) + 1];
1235 WCHAR newpcw[(newpc.get_nt_native_path ()->Length / sizeof (WCHAR))
1237 if (!CopyFileW (pc.get_wide_win32_path (pcw),
1238 newpc.get_wide_win32_path (newpcw), TRUE))
1243 SetFileAttributesW (newpcw, pc.file_attributes ());
1247 __seterrno_from_nt_status (status);
1255 fhandler_disk_file::utimens (const struct timespec *tvp)
1257 return utimens_fs (tvp);
1261 fhandler_base::utimens_fs (const struct timespec *tvp)
1263 LARGE_INTEGER lastaccess, lastwrite;
1264 struct timespec timeofday;
1265 struct timespec tmp[2];
1266 bool closeit = false;
1270 query_open (query_write_attributes);
1271 if (!open_fs (O_BINARY, 0))
1273 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1274 to change the timestamps. Unfortunately it's not sufficient for a
1275 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1276 for writing, though this fails for R/O files of course. */
1277 query_open (no_query);
1278 if (!open_fs (O_WRONLY | O_BINARY, 0))
1280 syscall_printf ("Opening file failed");
1287 gettimeofday (reinterpret_cast<struct timeval *> (&timeofday), 0);
1288 timeofday.tv_nsec *= 1000;
1290 tmp[1] = tmp[0] = timeofday;
1293 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1294 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1299 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1300 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1302 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1303 timespec_to_filetime (&tmp[0], (FILETIME *) &lastaccess);
1304 timespec_to_filetime (&tmp[1], (FILETIME *) &lastwrite);
1305 debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
1308 FILE_BASIC_INFORMATION fbi;
1309 fbi.CreationTime.QuadPart = 0LL;
1310 fbi.LastAccessTime = lastaccess;
1311 fbi.LastWriteTime = lastwrite;
1312 fbi.ChangeTime.QuadPart = 0LL;
1313 fbi.FileAttributes = 0;
1314 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1315 FileBasicInformation);
1318 /* Opening a directory on a 9x share from a NT machine works(!), but
1319 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1320 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1322 __seterrno_from_nt_status (status);
1328 fhandler_disk_file::fhandler_disk_file () :
1333 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1340 fhandler_disk_file::open (int flags, mode_t mode)
1342 return open_fs (flags, mode);
1346 fhandler_base::open_fs (int flags, mode_t mode)
1348 /* Unfortunately NT allows to open directories for writing, but that's
1349 disallowed according to SUSv3. */
1350 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1356 int res = fhandler_base::open (flags | O_DIROPEN, mode);
1360 /* This is for file systems known for having a buggy CreateFile call
1361 which might return a valid HANDLE without having actually opened
1363 The only known file system to date is the SUN NFS Solstice Client 3.1
1364 which returns a valid handle when trying to open a file in a nonexistent
1366 if (pc.has_buggy_open () && !pc.exists ())
1368 debug_printf ("Buggy open detected.");
1374 ino = get_ino_by_handle (pc, get_handle ());
1375 /* A unique ID is necessary to recognize fhandler entries which are
1376 duplicated by dup(2) or fork(2). */
1377 AllocateLocallyUniqueId ((PLUID) &unique_id);
1380 syscall_printf ("%d = fhandler_disk_file::open (%S, %p)", res,
1381 pc.get_nt_native_path (), flags);
1386 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
1389 _off64_t curpos = lseek (0, SEEK_CUR);
1390 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1394 size_t tmp_count = count;
1395 read (buf, tmp_count);
1396 if (lseek (curpos, SEEK_SET) >= 0)
1397 res = (ssize_t) tmp_count;
1401 debug_printf ("%d = pread (%p, %d, %d)\n", res, buf, count, offset);
1406 fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
1409 _off64_t curpos = lseek (0, SEEK_CUR);
1410 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1414 res = (ssize_t) write (buf, count);
1415 if (lseek (curpos, SEEK_SET) < 0)
1418 debug_printf ("%d = pwrite (%p, %d, %d)\n", res, buf, count, offset);
1423 fhandler_disk_file::mkdir (mode_t mode)
1426 SECURITY_ATTRIBUTES sa = sec_none_nih;
1427 security_descriptor sd;
1429 /* See comments in fhander_base::open () for an explanation why we defer
1430 setting security attributes on remote files. */
1431 if (has_acls () && !pc.isremote ())
1432 set_security_attribute (pc, S_IFDIR | ((mode & 07777) & ~cygheap->umask),
1437 OBJECT_ATTRIBUTES attr;
1439 PFILE_FULL_EA_INFORMATION p = NULL;
1442 if (pc.fs_is_nfs ())
1444 /* When creating a dir on an NFS share, we have to set the
1445 file mode by writing a NFS fattr3 structure with the
1446 correct mode bits set. */
1447 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1449 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1450 p->NextEntryOffset = 0;
1452 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1453 p->EaValueLength = sizeof (fattr3);
1454 strcpy (p->EaName, NFS_V3_ATTR);
1455 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1456 memset (nfs_attr, 0, sizeof (fattr3));
1457 nfs_attr->type = NF3DIR;
1458 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1460 status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
1461 pc.get_object_attr (attr, sa), &io, NULL,
1462 FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
1464 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1465 | FILE_OPEN_FOR_BACKUP_INTENT,
1467 if (NT_SUCCESS (status))
1469 if (has_acls () && pc.isremote ())
1470 set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
1471 S_IFDIR | ((mode & 07777) & ~cygheap->umask));
1476 __seterrno_from_nt_status (status);
1482 fhandler_disk_file::rmdir ()
1484 extern NTSTATUS unlink_nt (path_conv &pc);
1488 set_errno (ENOTDIR);
1497 NTSTATUS status = unlink_nt (pc);
1499 /* Check for existence of remote dirs after trying to delete them.
1501 - Sometimes SMB indicates failure when it really succeeds.
1502 - Removeing a directory on a samba drive doesn't return an error if the
1503 directory can't be removed because it's not empty. */
1506 OBJECT_ATTRIBUTES attr;
1507 FILE_BASIC_INFORMATION fbi;
1509 if (NT_SUCCESS (NtQueryAttributesFile
1510 (pc.get_object_attr (attr, sec_none_nih), &fbi)))
1511 status = STATUS_DIRECTORY_NOT_EMPTY;
1513 status = STATUS_SUCCESS;
1515 if (!NT_SUCCESS (status))
1517 __seterrno_from_nt_status (status);
1523 /* This is the minimal number of entries which fit into the readdir cache.
1524 The number of bytes allocated by the cache is determined by this number,
1525 To tune caching, just tweak this number. To get a feeling for the size,
1526 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1528 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1530 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1531 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1532 + (NAME_MAX + 1) * sizeof (WCHAR)))
1536 char __cache[DIR_BUF_SIZE]; /* W2K needs this buffer 8 byte aligned. */
1540 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1541 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1543 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1546 fhandler_disk_file::opendir (int fd)
1552 set_errno (ENOTDIR);
1553 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1555 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1561 else if ((dir->__d_dirent =
1562 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1570 if (cfd < 0 && fd < 0)
1573 dir->__d_dirent->__d_version = __DIRENT_VERSION;
1574 dir->__d_cookie = __DIRENT_COOKIE;
1575 dir->__handle = INVALID_HANDLE_VALUE;
1576 dir->__d_position = 0;
1577 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1578 ? dirent_isroot : 0;
1579 dir->__d_internal = (unsigned) new __DIR_mounts (get_name ());
1580 d_cachepos (dir) = 0;
1582 if (!pc.iscygdrive ())
1586 /* opendir() case. Initialize with given directory name and
1587 NULL directory handle. */
1588 OBJECT_ATTRIBUTES attr;
1592 status = NtOpenFile (&get_handle (),
1593 SYNCHRONIZE | FILE_LIST_DIRECTORY,
1594 pc.get_object_attr (attr, sec_none_nih),
1595 &io, FILE_SHARE_VALID_FLAGS,
1596 FILE_SYNCHRONOUS_IO_NONALERT
1597 | FILE_OPEN_FOR_BACKUP_INTENT
1598 | FILE_DIRECTORY_FILE);
1599 if (!NT_SUCCESS (status))
1601 __seterrno_from_nt_status (status);
1606 /* FileIdBothDirectoryInformation is apparently unsupported on
1607 XP when accessing directories on UDF. When trying to use it
1608 so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1609 It's not clear if the call isn't also unsupported on other
1610 OS/FS combinations (say, Win2K/CDFS or so). Instead of
1611 testing in readdir for yet another error code, let's use
1612 FileIdBothDirectoryInformation only on filesystems supporting
1613 persistent ACLs, FileDirectoryInformation otherwise.
1615 NFS clients hide dangling symlinks from directory queries,
1616 unless you use the FileNamesInformation info class.
1617 On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
1618 works fine, but only if the NFS share is mounted to a drive
1619 letter. TODO: We don't test that here for now, but it might
1620 be worth to test if there's a speed gain in using
1621 FileIdBothDirectoryInformation, because it doesn't require to
1622 open the file to read the inode number. */
1623 if (pc.hasgood_inode ())
1625 dir->__flags |= dirent_set_d_ino;
1626 if (pc.fs_is_nfs ())
1627 dir->__flags |= dirent_nfs_d_ino;
1628 else if (wincap.has_fileid_dirinfo ()
1629 && !pc.has_buggy_fileid_dirinfo ())
1630 dir->__flags |= dirent_get_d_ino;
1637 /* Filling cfd with `this' (aka storing this in the file
1638 descriptor table should only happen after it's clear that
1639 opendir doesn't fail, otherwise we end up cfree'ing the
1640 fhandler twice, once in opendir() in dir.cc, the second
1641 time on exit. Nasty, nasty... */
1644 if (pc.iscygdrive ())
1645 cfd->nohandle (true);
1647 set_close_on_exec (true);
1652 syscall_printf ("%p = opendir (%s)", res, get_name ());
1656 delete d_mounts (dir);
1658 free (dir->__d_dirent);
1660 free (dir->__d_dirname);
1667 readdir_get_ino (const char *path, bool dot_dot)
1672 OBJECT_ATTRIBUTES attr;
1678 fname = (char *) alloca (strlen (path) + 4);
1679 char *c = stpcpy (fname, path);
1685 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN);
1686 if (pc.isspecial ())
1688 if (!stat_worker (pc, &st))
1691 else if (!pc.hasgood_inode ())
1692 ino = hash_path_name (0, pc.get_nt_native_path ());
1693 else if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1694 pc.get_object_attr (attr, sec_none_nih),
1695 &io, FILE_SHARE_VALID_FLAGS,
1696 FILE_OPEN_FOR_BACKUP_INTENT
1697 | (pc.is_rep_symlink ()
1698 ? FILE_OPEN_REPARSE_POINT : 0))))
1700 ino = get_ino_by_handle (pc, hdl);
1702 ino = hash_path_name (0, pc.get_nt_native_path ());
1709 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1710 DWORD attr, PUNICODE_STRING fname)
1715 if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
1718 return geterrno_from_win_error (w32_err);
1721 dir->__flags &= ~dirent_set_d_ino;
1724 /* Set d_type if type can be determined from file attributes.
1725 FILE_ATTRIBUTE_SYSTEM ommitted to leave DT_UNKNOWN for old symlinks.
1726 For new symlinks, d_type will be reset to DT_UNKNOWN below. */
1728 !(attr & ( ~FILE_ATTRIBUTE_VALID_FLAGS
1729 | FILE_ATTRIBUTE_SYSTEM
1730 | FILE_ATTRIBUTE_REPARSE_POINT)))
1732 if (attr & FILE_ATTRIBUTE_DIRECTORY)
1733 de->d_type = DT_DIR;
1735 de->d_type = DT_REG;
1738 /* Check for directory reparse point. These are potential volume mount
1739 points which have another inode than the underlying directory. */
1740 if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1741 == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1744 OBJECT_ATTRIBUTES attr;
1747 InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
1748 get_handle (), NULL);
1749 if (is_volume_mountpoint (&attr)
1750 && (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
1751 FILE_SHARE_VALID_FLAGS,
1752 FILE_OPEN_FOR_BACKUP_INTENT))))
1754 de->d_ino = get_ino_by_handle (pc, reph);
1759 /* Check for Windows shortcut. If it's a Cygwin or U/WIN
1760 symlink, drop the .lnk suffix. */
1761 if ((attr & FILE_ATTRIBUTE_READONLY) && fname->Length > 4 * sizeof (WCHAR))
1763 UNICODE_STRING uname;
1765 RtlInitCountedUnicodeString (&uname,
1767 + fname->Length / sizeof (WCHAR) - 4,
1768 4 * sizeof (WCHAR));
1769 if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
1772 UNICODE_STRING fbuf;
1775 RtlCopyUnicodeString (&fbuf, pc.get_nt_native_path ());
1776 RtlAppendUnicodeToString (&fbuf, L"\\");
1777 RtlAppendUnicodeStringToString (&fbuf, fname);
1778 fbuf.Buffer += 4; /* Skip leading \??\ */
1779 fbuf.Length -= 4 * sizeof (WCHAR);
1780 if (fbuf.Buffer[1] != L':') /* UNC path */
1782 *(fbuf.Buffer += 2) = L'\\';
1783 fbuf.Length -= 2 * sizeof (WCHAR);
1785 path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
1786 if (fpath.issymlink () || fpath.is_fs_special ())
1788 fname->Length -= 4 * sizeof (WCHAR);
1789 de->d_type = DT_UNKNOWN;
1794 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
1795 fname->Length / sizeof (WCHAR));
1797 if (dir->__d_position == 0 && !strcmp (de->d_name, "."))
1798 dir->__flags |= dirent_saw_dot;
1799 else if (dir->__d_position == 1 && !strcmp (de->d_name, ".."))
1800 dir->__flags |= dirent_saw_dot_dot;
1805 fhandler_disk_file::readdir (DIR *dir, dirent *de)
1808 NTSTATUS status = STATUS_SUCCESS;
1809 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
1811 ULONG FileNameLength;
1812 ULONG FileAttributes = 0;
1814 UNICODE_STRING fname;
1816 /* d_cachepos always refers to the next cache entry to use. If it's 0
1817 we must reload the cache. */
1818 if (d_cachepos (dir) == 0)
1820 if ((dir->__flags & dirent_get_d_ino))
1822 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1823 d_cache (dir), DIR_BUF_SIZE,
1824 FileIdBothDirectoryInformation,
1825 FALSE, NULL, dir->__d_position == 0);
1826 /* FileIdBothDirectoryInformation isn't supported for remote drives
1827 on NT4 and 2K systems, and it's also not supported on 2K at all,
1828 when accessing network drives on any remote OS. We just fall
1829 back to using a standard directory query in this case and note
1830 this case using the dirent_get_d_ino flag. */
1831 if (status == STATUS_INVALID_LEVEL
1832 || status == STATUS_INVALID_PARAMETER
1833 || status == STATUS_INVALID_INFO_CLASS)
1834 dir->__flags &= ~dirent_get_d_ino;
1835 /* Something weird happens on Samba up to version 3.0.21c, which is
1836 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
1837 nicely, but only up to the 128th entry in the directory. After
1838 reaching this entry, the next call to NtQueryDirectoryFile
1839 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
1840 Why should we care, we can just switch to FileDirectoryInformation,
1841 isn't it? Nope! The next call to
1842 NtQueryDirectoryFile(FileDirectoryInformation)
1843 actually returns STATUS_NO_MORE_FILES, regardless how many files
1844 are left unread in the directory. This does not happen when using
1845 FileDirectoryInformation right from the start, but since
1846 we can't decide whether the server we're talking with has this
1847 bug or not, we end up serving Samba shares always in the slow
1848 mode using FileDirectoryInformation. So, what we do here is
1849 to implement the solution suggested by Andrew Tridgell, we just
1850 reread all entries up to dir->d_position using
1851 FileDirectoryInformation.
1852 However, We do *not* mark this server as broken and fall back to
1853 using FileDirectoryInformation further on. This would slow
1854 down every access to such a server, even for directories under
1855 128 entries. Also, bigger dirs only suffer from one additional
1856 call per full directory scan, which shouldn't be too big a hit.
1857 This can easily be changed if necessary. */
1858 if (status == STATUS_INVALID_LEVEL && dir->__d_position)
1860 d_cachepos (dir) = 0;
1861 for (int cnt = 0; cnt < dir->__d_position; ++cnt)
1863 if (d_cachepos (dir) == 0)
1865 status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
1866 NULL, &io, d_cache (dir),
1868 FileDirectoryInformation,
1869 FALSE, NULL, cnt == 0);
1870 if (!NT_SUCCESS (status))
1873 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
1874 + d_cachepos (dir));
1875 if (buf->NextEntryOffset == 0)
1876 d_cachepos (dir) = 0;
1878 d_cachepos (dir) += buf->NextEntryOffset;
1883 if (!(dir->__flags & dirent_get_d_ino))
1884 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1885 d_cache (dir), DIR_BUF_SIZE,
1886 (dir->__flags & dirent_nfs_d_ino)
1887 ? FileNamesInformation
1888 : FileDirectoryInformation,
1889 FALSE, NULL, dir->__d_position == 0);
1894 if (status == STATUS_NO_MORE_FILES)
1896 else if (!NT_SUCCESS (status))
1897 debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
1898 status, RtlNtStatusToDosError (status));
1901 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
1902 if (buf->NextEntryOffset == 0)
1903 d_cachepos (dir) = 0;
1905 d_cachepos (dir) += buf->NextEntryOffset;
1906 if ((dir->__flags & dirent_get_d_ino))
1908 FileName = buf->FileName;
1909 FileNameLength = buf->FileNameLength;
1910 FileAttributes = buf->FileAttributes;
1911 if ((dir->__flags & dirent_set_d_ino))
1912 de->d_ino = buf->FileId.QuadPart;
1914 else if ((dir->__flags & dirent_nfs_d_ino))
1916 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
1917 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
1921 FileName = ((PFILE_DIRECTORY_INFORMATION) buf)->FileName;
1922 FileNameLength = ((PFILE_DIRECTORY_INFORMATION) buf)->FileNameLength;
1923 FileAttributes = ((PFILE_DIRECTORY_INFORMATION) buf)->FileAttributes;
1925 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
1926 de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
1927 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
1929 OBJECT_ATTRIBUTES attr;
1931 if (dir->__d_position == 0 && FileNameLength == 2
1932 && FileName[0] == '.')
1933 de->d_ino = get_ino_by_handle (pc, get_handle ());
1934 else if (dir->__d_position == 1 && FileNameLength == 4
1935 && FileName[0] == L'.' && FileName[1] == L'.')
1936 if (!(dir->__flags & dirent_isroot))
1937 de->d_ino = readdir_get_ino (get_name (), true);
1939 de->d_ino = get_ino_by_handle (pc, get_handle ());
1944 InitializeObjectAttributes (&attr, &fname,
1945 pc.objcaseinsensitive (),
1946 get_handle (), NULL);
1947 if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
1948 FILE_SHARE_VALID_FLAGS,
1949 FILE_OPEN_FOR_BACKUP_INTENT
1950 | FILE_OPEN_REPARSE_POINT)))
1952 de->d_ino = get_ino_by_handle (pc, hdl);
1956 /* Untrusted file system. Don't try to fetch inode number again. */
1958 dir->__flags &= ~dirent_set_d_ino;
1962 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
1963 buf ? FileAttributes : 0, &fname)))
1964 dir->__d_position++;
1965 else if (!(dir->__flags & dirent_saw_dot))
1967 strcpy (de->d_name , ".");
1968 de->d_ino = get_ino_by_handle (pc, get_handle ());
1969 dir->__d_position++;
1970 dir->__flags |= dirent_saw_dot;
1973 else if (!(dir->__flags & dirent_saw_dot_dot))
1975 strcpy (de->d_name , "..");
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 ());
1980 dir->__d_position++;
1981 dir->__flags |= dirent_saw_dot_dot;
1985 syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, &de, res ? "***" : de->d_name);
1990 fhandler_disk_file::telldir (DIR *dir)
1992 return dir->__d_position;
1996 fhandler_disk_file::seekdir (DIR *dir, _off64_t loc)
1999 while (loc > dir->__d_position)
2000 if (!::readdir (dir))
2005 fhandler_disk_file::rewinddir (DIR *dir)
2007 d_cachepos (dir) = 0;
2008 if (wincap.has_buggy_restart_scan () && isremote ())
2010 /* This works around a W2K bug. The RestartScan parameter in calls
2011 to NtQueryDirectoryFile on remote shares is ignored, thus
2012 resulting in not being able to rewind on remote shares. By
2013 reopening the directory, we get a fresh new directory pointer. */
2014 OBJECT_ATTRIBUTES attr;
2019 InitializeObjectAttributes (&attr, &ro_u_empty, pc.objcaseinsensitive (),
2020 get_handle (), NULL);
2021 status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2022 &attr, &io, FILE_SHARE_VALID_FLAGS,
2023 FILE_SYNCHRONOUS_IO_NONALERT
2024 | FILE_OPEN_FOR_BACKUP_INTENT
2025 | FILE_DIRECTORY_FILE);
2026 if (!NT_SUCCESS (stat))
2027 debug_printf ("Unable to reopen dir %s, NT error: %p",
2028 get_name (), status);
2031 NtClose (get_handle ());
2032 set_io_handle (new_dir);
2035 dir->__d_position = 0;
2036 d_mounts (dir)->rewind ();
2040 fhandler_disk_file::closedir (DIR *dir)
2045 delete d_mounts (dir);
2048 else if (get_handle () == INVALID_HANDLE_VALUE)
2053 else if (!NT_SUCCESS (status = NtClose (get_handle ())))
2055 __seterrno_from_nt_status (status);
2058 syscall_printf ("%d = closedir (%p, %s)", res, dir, get_name ());
2062 fhandler_cygdrive::fhandler_cygdrive () :
2063 fhandler_disk_file (), ndrives (0), pdrive (NULL)
2068 fhandler_cygdrive::open (int flags, mode_t mode)
2070 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2075 if (flags & O_WRONLY)
2087 fhandler_cygdrive::close ()
2092 #define DRVSZ sizeof ("x:\\")
2094 fhandler_cygdrive::set_drives ()
2096 const int len = 2 + 26 * DRVSZ;
2097 char *p = const_cast<char *> (get_win32_name ());
2099 ndrives = GetLogicalDriveStrings (len, p) / DRVSZ;
2103 fhandler_cygdrive::fstat (struct __stat64 *buf)
2105 fhandler_base::fstat (buf);
2107 buf->st_mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
2110 char flptst[] = "X:";
2112 for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
2113 if (is_floppy ((flptst[0] = *p, flptst))
2114 || GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
2116 buf->st_nlink = n + 2;
2121 fhandler_cygdrive::opendir (int fd)
2125 dir = fhandler_disk_file::opendir (fd);
2126 if (dir && !ndrives)
2133 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2135 char flptst[] = "X:";
2139 if (!pdrive || !*pdrive)
2141 if (!(dir->__flags & dirent_saw_dot))
2143 de->d_name[0] = '.';
2144 de->d_name[1] = '\0';
2149 if (!is_floppy ((flptst[0] = *pdrive, flptst))
2150 && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2152 pdrive = strchr (pdrive, '\0') + 1;
2154 *de->d_name = cyg_tolower (*pdrive);
2155 de->d_name[1] = '\0';
2156 user_shared->warned_msdos = true;
2157 de->d_ino = readdir_get_ino (pdrive, false);
2158 dir->__d_position++;
2159 pdrive = strchr (pdrive, '\0') + 1;
2160 syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2165 fhandler_cygdrive::rewinddir (DIR *dir)
2167 pdrive = get_win32_name ();
2168 dir->__d_position = 0;
2172 fhandler_cygdrive::closedir (DIR *dir)
2174 pdrive = get_win32_name ();