1 /* fhandler_disk_file.cc
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
15 #include <sys/statvfs.h>
22 #include "shared_info.h"
29 #define _COMPILING_NEWLIB
35 const char *parent_dir;
37 UNICODE_STRING mounts[MAX_MOUNTS];
38 bool found[MAX_MOUNTS + 2];
39 UNICODE_STRING cygdrive;
41 #define __DIR_PROC (MAX_MOUNTS)
42 #define __DIR_CYGDRIVE (MAX_MOUNTS+1)
44 __ino64_t eval_ino (int idx)
47 char fname[parent_dir_len + mounts[idx].Length + 2];
50 char *c = stpcpy (fname, parent_dir);
53 sys_wcstombs (c, mounts[idx].Length + 1,
54 mounts[idx].Buffer, mounts[idx].Length / sizeof (WCHAR));
55 path_conv pc (fname, PC_SYM_NOFOLLOW | PC_POSIX | PC_KEEP_HANDLE);
56 if (!stat_worker (pc, &st))
62 __DIR_mounts (const char *posix_path)
63 : parent_dir (posix_path)
65 parent_dir_len = strlen (parent_dir);
66 count = mount_table->get_mounts_here (parent_dir, parent_dir_len, mounts,
72 for (int i = 0; i < count; ++i)
73 RtlFreeUnicodeString (&mounts[i]);
74 RtlFreeUnicodeString (&cygdrive);
76 __ino64_t check_mount (PUNICODE_STRING fname, __ino64_t ino,
79 if (parent_dir_len == 1) /* root dir */
81 if (RtlEqualUnicodeString (fname, &ro_u_proc, FALSE))
83 found[__DIR_PROC] = true;
86 if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2
87 && RtlEqualUnicodeString (fname, &cygdrive, FALSE))
89 found[__DIR_CYGDRIVE] = true;
93 for (int i = 0; i < count; ++i)
94 if (RtlEqualUnicodeString (fname, &mounts[i], FALSE))
97 return eval ? eval_ino (i) : 1;
101 __ino64_t check_missing_mount (PUNICODE_STRING retname = NULL)
103 for (int i = 0; i < count; ++i)
109 *retname = mounts[i];
114 if (parent_dir_len == 1) /* root dir */
116 if (!found[__DIR_PROC])
118 found[__DIR_PROC] = true;
120 *retname = ro_u_proc;
123 if (!found[__DIR_CYGDRIVE])
125 found[__DIR_CYGDRIVE] = true;
126 if (cygdrive.Length > 0)
136 void rewind () { memset (found, 0, sizeof found); }
140 path_conv::isgood_inode (__ino64_t ino) const
142 /* We can't trust remote inode numbers of only 32 bit. That means,
143 remote NT4 NTFS, as well as shares of Samba version < 3.0.
144 The known exception are SFU NFS shares, which return the valid 32 bit
145 inode number from the remote file system unchanged. */
146 return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ());
149 /* Check reparse point for type. IO_REPARSE_TAG_MOUNT_POINT types are
150 either volume mount points, which are treated as directories, or they
151 are directory mount points, which are treated as symlinks.
152 IO_REPARSE_TAG_SYMLINK types are always symlinks. We don't know
153 anything about other reparse points, so they are treated as unknown. */
155 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr)
157 DWORD ret = DT_UNKNOWN;
160 UNICODE_STRING subst;
162 if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, attr, &io,
163 FILE_SHARE_VALID_FLAGS,
164 FILE_OPEN_FOR_BACKUP_INTENT
165 | FILE_OPEN_REPARSE_POINT)))
167 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER)
168 alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
169 if (NT_SUCCESS (NtFsControlFile (reph, NULL, NULL, NULL,
170 &io, FSCTL_GET_REPARSE_POINT, NULL, 0,
171 (LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE)))
173 if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
175 RtlInitCountedUnicodeString (&subst,
176 (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
177 + rp->MountPointReparseBuffer.SubstituteNameOffset),
178 rp->MountPointReparseBuffer.SubstituteNameLength);
179 /* Only volume mountpoints are treated as directories. */
180 if (RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))
185 else if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
194 path_conv::get_ino_by_handle (HANDLE hdl)
197 FILE_INTERNAL_INFORMATION fai;
199 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
200 FileInternalInformation))
201 && isgood_inode (fai.FileId.QuadPart))
202 return fai.FileId.QuadPart;
207 /* This function is obsolete. We're keeping it in so we don't forget
208 that we already did all that at one point. */
210 path_conv::ndisk_links (DWORD nNumberOfLinks)
212 if (!isdir () || isremote ())
213 return nNumberOfLinks;
215 OBJECT_ATTRIBUTES attr;
219 if (!NT_SUCCESS (NtOpenFile (&fh, SYNCHRONIZE | FILE_LIST_DIRECTORY,
220 get_object_attr (attr, sec_none_nih),
221 &io, FILE_SHARE_VALID_FLAGS,
222 FILE_SYNCHRONOUS_IO_NONALERT
223 | FILE_OPEN_FOR_BACKUP_INTENT
224 | FILE_DIRECTORY_FILE)))
225 return nNumberOfLinks;
229 PFILE_BOTH_DIRECTORY_INFORMATION fdibuf = (PFILE_BOTH_DIRECTORY_INFORMATION)
231 __DIR_mounts *dir = new __DIR_mounts (normalized_path);
232 while (NT_SUCCESS (NtQueryDirectoryFile (fh, NULL, NULL, NULL, &io, fdibuf,
233 65536, FileBothDirectoryInformation,
234 FALSE, NULL, first)))
239 /* All directories have . and .. as their first entries.
240 If . is not present as first entry, we're on a drive's
241 root direcotry, which doesn't have these entries. */
242 if (fdibuf->FileNameLength != 2 || fdibuf->FileName[0] != L'.')
245 for (PFILE_BOTH_DIRECTORY_INFORMATION pfdi = fdibuf;
247 pfdi = (PFILE_BOTH_DIRECTORY_INFORMATION)
248 (pfdi->NextEntryOffset ? (PBYTE) pfdi + pfdi->NextEntryOffset
251 switch (pfdi->FileAttributes
252 & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
254 case FILE_ATTRIBUTE_DIRECTORY:
255 /* Just a directory */
258 case FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT:
259 /* Volume mount point or symlink to directory */
261 UNICODE_STRING fname;
263 RtlInitCountedUnicodeString (&fname, pfdi->FileName,
264 pfdi->FileNameLength);
265 InitializeObjectAttributes (&attr, &fname,
266 objcaseinsensitive (), fh, NULL);
267 if (is_volume_mountpoint (&attr))
274 UNICODE_STRING fname;
275 RtlInitCountedUnicodeString (&fname, pfdi->FileName,
276 pfdi->FileNameLength);
277 dir->check_mount (&fname, 0, false);
280 while (dir->check_missing_mount ())
288 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
289 This returns the content of a struct fattr3 as defined in RFC 1813.
290 The content is the NFS equivalent of struct stat. so there's not much
291 to do here except for copying. */
293 fhandler_base::fstat_by_nfs_ea (struct __stat64 *buf)
295 fattr3 *nfs_attr = pc.nfsattr ();
297 if (get_io_handle ())
299 /* NFS stumbles over its own caching. If you write to the file,
300 a subsequent fstat does not return the actual size of the file,
301 but the size at the time the handle has been opened. Unless
302 access through another handle invalidates the caching within the
304 if (get_access () & GENERIC_WRITE)
305 FlushFileBuffers (get_io_handle ());
306 nfs_fetch_fattr3 (get_io_handle (), nfs_attr);
308 buf->st_dev = nfs_attr->fsid;
309 buf->st_ino = nfs_attr->fileid;
310 buf->st_mode = (nfs_attr->mode & 0xfff)
311 | nfs_type_mapping[nfs_attr->type & 7];
312 buf->st_nlink = nfs_attr->nlink;
313 /* FIXME: How to convert UNIX uid/gid to Windows SIDs? */
315 buf->st_uid = nfs_attr->uid;
316 buf->st_gid = nfs_attr->gid;
318 buf->st_uid = myself->uid;
319 buf->st_gid = myself->gid;
321 buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
322 nfs_attr->rdev.specdata2);
323 buf->st_size = nfs_attr->size;
324 buf->st_blksize = PREFERRED_IO_BLKSIZE;
325 buf->st_blocks = (nfs_attr->used + S_BLKSIZE - 1) / S_BLKSIZE;
326 buf->st_atim = nfs_attr->atime;
327 buf->st_mtim = nfs_attr->mtime;
328 buf->st_ctim = nfs_attr->ctime;
333 fhandler_base::fstat_by_handle (struct __stat64 *buf)
335 /* Don't use FileAllInformation info class. It returns a pathname rather
336 than a filename, so it needs a really big buffer for no good reason
337 since we don't need the name anyway. So we just call the three info
338 classes necessary to get all information required by stat(2). */
339 FILE_STANDARD_INFORMATION fsi;
340 FILE_INTERNAL_INFORMATION fii;
342 HANDLE h = get_stat_handle ();
346 /* If the file has been opened for other purposes than stat, we can't rely
347 on the information stored in pc.fnoi. So we overwrite them here. */
348 if (get_io_handle ())
350 status = file_get_fnoi (h, pc.fs_is_netapp (), pc.fnoi ());
351 if (!NT_SUCCESS (status))
353 debug_printf ("%p = NtQueryInformationFile(%S, "
354 "FileNetworkOpenInformation)",
355 status, pc.get_nt_native_path ());
359 if (!pc.hasgood_inode ())
360 fsi.NumberOfLinks = 1;
363 status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
364 FileStandardInformation);
365 if (!NT_SUCCESS (status))
367 debug_printf ("%p = NtQueryInformationFile(%S, "
368 "FileStandardInformation)",
369 status, pc.get_nt_native_path ());
374 status = NtQueryInformationFile (h, &io, &fii, sizeof fii,
375 FileInternalInformation);
376 if (!NT_SUCCESS (status))
378 debug_printf ("%p = NtQueryInformationFile(%S, "
379 "FileInternalInformation)",
380 status, pc.get_nt_native_path ());
383 else if (pc.isgood_inode (fii.FileId.QuadPart))
384 ino = fii.FileId.QuadPart;
387 return fstat_helper (buf, fsi.NumberOfLinks);
391 fhandler_base::fstat_by_name (struct __stat64 *buf)
394 OBJECT_ATTRIBUTES attr;
396 UNICODE_STRING dirname;
397 UNICODE_STRING basename;
400 FILE_ID_BOTH_DIR_INFORMATION fdi;
401 WCHAR buf[NAME_MAX + 1];
404 if (!ino && pc.hasgood_inode () && !pc.has_buggy_fileid_dirinfo ())
406 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
407 InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
409 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
410 &attr, &io, FILE_SHARE_VALID_FLAGS,
411 FILE_SYNCHRONOUS_IO_NONALERT
412 | FILE_OPEN_FOR_BACKUP_INTENT
413 | FILE_DIRECTORY_FILE);
414 if (!NT_SUCCESS (status))
415 debug_printf ("%p = NtOpenFile(%S)", status,
416 pc.get_nt_native_path ());
419 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
420 &fdi_buf.fdi, sizeof fdi_buf,
421 FileIdBothDirectoryInformation,
422 TRUE, &basename, TRUE);
424 if (!NT_SUCCESS (status))
425 debug_printf ("%p = NtQueryDirectoryFile(%S)", status,
426 pc.get_nt_native_path ());
428 ino = fdi_buf.fdi.FileId.QuadPart;
431 return fstat_helper (buf, 1);
435 fhandler_base::fstat_fs (struct __stat64 *buf)
439 int open_flags = O_RDONLY | O_BINARY;
441 if (get_stat_handle ())
443 if (!nohandle () && !is_fs_special ())
444 res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
446 res = fstat_by_name (buf);
449 /* First try to open with generic read access. This allows to read the file
450 in fstat_helper (when checking for executability) without having to
451 re-open it. Opening a file can take a lot of time on network drives
452 so we try to avoid that. */
453 oret = open_fs (open_flags, 0);
456 query_open (query_read_attributes);
457 oret = open_fs (open_flags, 0);
461 /* We now have a valid handle, regardless of the "nohandle" state.
462 Since fhandler_base::open only calls CloseHandle if !nohandle,
463 we have to set it to false before calling close and restore
464 the state afterwards. */
465 res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
466 bool no_handle = nohandle ();
469 nohandle (no_handle);
470 set_io_handle (NULL);
473 res = fstat_by_name (buf);
479 fhandler_base::fstat_helper (struct __stat64 *buf,
480 DWORD nNumberOfLinks)
483 FILE_COMPRESSION_INFORMATION fci;
484 HANDLE h = get_stat_handle ();
485 PFILE_NETWORK_OPEN_INFORMATION pfnoi = pc.fnoi ();
486 ULONG attributes = pc.file_attributes ();
488 to_timestruc_t ((PFILETIME) &pfnoi->LastAccessTime, &buf->st_atim);
489 to_timestruc_t ((PFILETIME) &pfnoi->LastWriteTime, &buf->st_mtim);
490 /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
491 (FAT for instance). If so, it's faked using LastWriteTime. */
492 to_timestruc_t (pfnoi->ChangeTime.QuadPart ? (PFILETIME) &pfnoi->ChangeTime
493 : (PFILETIME) &pfnoi->LastWriteTime,
495 to_timestruc_t ((PFILETIME) &pfnoi->CreationTime, &buf->st_birthtim);
496 buf->st_rdev = buf->st_dev = get_dev ();
497 /* CV 2011-01-13: Observations on the Cygwin mailing list point to an
498 interesting behaviour in some Windows versions. Apparently the size of
499 a directory is computed at the time the directory is first scanned. This
500 can result in two subsequent NtQueryInformationFile calls to return size
501 0 in the first call and size > 0 in the second call. This in turn can
502 affect applications like newer tar.
503 FIXME: Is the allocation size affected as well? */
504 buf->st_size = pc.isdir () ? 0 : (_off64_t) pfnoi->EndOfFile.QuadPart;
505 /* The number of links to a directory includes the number of subdirectories
506 in the directory, since all those subdirectories point to it. However,
507 this is painfully slow, so we do without it. */
509 buf->st_nlink = pc.ndisk_links (nNumberOfLinks);
511 buf->st_nlink = nNumberOfLinks;
514 /* Enforce namehash as inode number on untrusted file systems. */
515 if (ino && pc.isgood_inode (ino))
516 buf->st_ino = (__ino64_t) ino;
518 buf->st_ino = get_ino ();
520 buf->st_blksize = PREFERRED_IO_BLKSIZE;
522 if (pfnoi->AllocationSize.QuadPart >= 0LL)
523 /* A successful NtQueryInformationFile returns the allocation size
524 correctly for compressed and sparse files as well. */
525 buf->st_blocks = (pfnoi->AllocationSize.QuadPart + S_BLKSIZE - 1)
527 else if (::has_attribute (attributes, FILE_ATTRIBUTE_COMPRESSED
528 | FILE_ATTRIBUTE_SPARSE_FILE)
529 && h && !is_fs_special ()
530 && !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci,
531 FileCompressionInformation))
532 /* Otherwise we request the actual amount of bytes allocated for
533 compressed and sparsed files. */
534 buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
537 /* Otherwise compute no. of blocks from file size. */
538 buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
541 /* Using a side effect: get_file_attributes checks for directory.
542 This is used, to set S_ISVTX, if needed. */
544 buf->st_mode = S_IFDIR;
545 else if (pc.issymlink ())
547 buf->st_size = pc.get_symlink_length ();
548 /* symlinks are everything for everyone! */
549 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
550 get_file_attribute (h, pc, NULL,
551 &buf->st_uid, &buf->st_gid);
554 else if (pc.issocket ())
555 buf->st_mode = S_IFSOCK;
557 if (!get_file_attribute (is_fs_special () && !pc.issocket () ? NULL : h, pc,
558 &buf->st_mode, &buf->st_uid, &buf->st_gid))
560 /* If read-only attribute is set, modify ntsec return value */
561 if (::has_attribute (attributes, FILE_ATTRIBUTE_READONLY)
562 && !pc.isdir () && !pc.issymlink ())
563 buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
565 if (buf->st_mode & S_IFMT)
567 else if (!is_fs_special ())
568 buf->st_mode |= S_IFREG;
571 buf->st_dev = buf->st_rdev = dev ();
572 buf->st_mode = dev ().mode;
578 buf->st_mode |= STD_RBITS;
580 if (!::has_attribute (attributes, FILE_ATTRIBUTE_READONLY))
581 buf->st_mode |= STD_WBITS;
582 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
585 buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
586 else if (buf->st_mode & S_IFMT)
588 else if (is_fs_special ())
590 buf->st_dev = buf->st_rdev = dev ();
591 buf->st_mode = dev ().mode;
596 buf->st_mode |= S_IFREG;
597 /* Check suffix for executable file. */
598 if (pc.exec_state () != is_executable)
600 PUNICODE_STRING path = pc.get_nt_native_path ();
602 if (RtlEqualUnicodePathSuffix (path, &ro_u_exe, TRUE)
603 || RtlEqualUnicodePathSuffix (path, &ro_u_lnk, TRUE)
604 || RtlEqualUnicodePathSuffix (path, &ro_u_com, TRUE))
607 /* No known suffix, check file header. This catches binaries and
609 if (pc.exec_state () == dont_know_if_executable)
611 OBJECT_ATTRIBUTES attr;
615 /* We have to re-open the file. Either the file is not opened
616 for reading, or the read will change the file position of the
618 pc.init_reopen_attr (&attr, h);
619 status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA,
620 &attr, &io, FILE_SHARE_VALID_FLAGS,
621 FILE_OPEN_FOR_BACKUP_INTENT
622 | FILE_SYNCHRONOUS_IO_NONALERT);
623 if (!NT_SUCCESS (status))
624 debug_printf ("%p = NtOpenFile(%S)", status,
625 pc.get_nt_native_path ());
628 LARGE_INTEGER off = { QuadPart:0LL };
631 status = NtReadFile (h, NULL, NULL, NULL,
632 &io, magic, 3, &off, NULL);
633 if (!NT_SUCCESS (status))
634 debug_printf ("%p = NtReadFile(%S)", status,
635 pc.get_nt_native_path ());
636 else if (has_exec_chars (magic, io.Information))
638 /* Heureka, it's an executable */
640 buf->st_mode |= STD_XBITS;
646 if (pc.exec_state () == is_executable)
647 buf->st_mode |= STD_XBITS;
649 /* This fakes the permissions of all files to match the current umask. */
650 buf->st_mode &= ~(cygheap->umask);
651 /* If the FS supports ACLs, we're here because we couldn't even open
652 the file for READ_CONTROL access. Chances are high that the file's
653 security descriptor has no ACE for "Everyone", so we should not fake
654 any access for "others". */
656 buf->st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH);
660 syscall_printf ("0 = fstat (%S, %p) st_size=%D, st_mode=%p, st_ino=%D"
661 "st_atim=%x.%x st_ctim=%x.%x "
662 "st_mtim=%x.%x st_birthtim=%x.%x",
663 pc.get_nt_native_path (), buf,
664 buf->st_size, buf->st_mode, buf->st_ino,
665 buf->st_atim.tv_sec, buf->st_atim.tv_nsec,
666 buf->st_ctim.tv_sec, buf->st_ctim.tv_nsec,
667 buf->st_mtim.tv_sec, buf->st_mtim.tv_nsec,
668 buf->st_birthtim.tv_sec, buf->st_birthtim.tv_nsec);
673 fhandler_disk_file::fstat (struct __stat64 *buf)
675 return fstat_fs (buf);
679 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
681 int ret = -1, opened = 0;
684 FILE_FS_FULL_SIZE_INFORMATION full_fsi;
685 FILE_FS_SIZE_INFORMATION fsi;
686 /* We must not use the stat handle here, even if it exists. The handle
687 has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
688 mount point, it points to the FS of the mount point, rather than to the
690 HANDLE fh = get_handle ();
694 OBJECT_ATTRIBUTES attr;
695 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
696 pc.get_object_attr (attr, sec_none_nih),
697 &io, FILE_SHARE_VALID_FLAGS,
698 FILE_OPEN_FOR_BACKUP_INTENT));
701 /* Can't open file. Try again with parent dir. */
702 UNICODE_STRING dirname;
703 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
704 attr.ObjectName = &dirname;
705 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
706 FILE_SHARE_VALID_FLAGS,
707 FILE_OPEN_FOR_BACKUP_INTENT));
713 sfs->f_files = ULONG_MAX;
714 sfs->f_ffree = ULONG_MAX;
715 sfs->f_favail = ULONG_MAX;
716 sfs->f_fsid = pc.fs_serial_number ();
717 sfs->f_flag = pc.fs_flags ();
718 sfs->f_namemax = pc.fs_name_len ();
719 /* Get allocation related information. Try to get "full" information
720 first, which is only available since W2K. If that fails, try to
721 retrieve normal allocation information. */
722 status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
723 FileFsFullSizeInformation);
724 if (NT_SUCCESS (status))
726 sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
727 sfs->f_frsize = sfs->f_bsize;
728 sfs->f_blocks = full_fsi.TotalAllocationUnits.LowPart;
729 sfs->f_bfree = full_fsi.ActualAvailableAllocationUnits.LowPart;
730 sfs->f_bavail = full_fsi.CallerAvailableAllocationUnits.LowPart;
731 if (sfs->f_bfree > sfs->f_bavail)
733 /* Quotas active. We can't trust TotalAllocationUnits. */
734 NTFS_VOLUME_DATA_BUFFER nvdb;
736 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
737 FSCTL_GET_NTFS_VOLUME_DATA,
738 NULL, 0, &nvdb, sizeof nvdb);
739 if (!NT_SUCCESS (status))
740 debug_printf ("%p = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
741 status, pc.get_nt_native_path ());
743 sfs->f_blocks = nvdb.TotalClusters.QuadPart;
749 status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
750 FileFsSizeInformation);
751 if (!NT_SUCCESS (status))
753 __seterrno_from_nt_status (status);
756 sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
757 sfs->f_frsize = sfs->f_bsize;
758 sfs->f_blocks = fsi.TotalAllocationUnits.LowPart;
759 sfs->f_bfree = fsi.AvailableAllocationUnits.LowPart;
760 sfs->f_bavail = sfs->f_bfree;
766 syscall_printf ("%d = fstatvfs(%s, %p)", ret, get_name (), sfs);
771 fhandler_disk_file::fchmod (mode_t mode)
773 extern int chmod_device (path_conv& pc, mode_t mode);
779 if (pc.is_fs_special ())
780 return chmod_device (pc, mode);
784 query_open (query_write_dac);
785 if (!(oret = open (O_BINARY, 0)))
787 /* Need WRITE_DAC|WRITE_OWNER to write ACLs. */
790 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
791 query_open (query_write_attributes);
792 if (!(oret = open (O_BINARY, 0)))
799 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
800 Only type and mode have to be set. Apparently type isn't checked
801 for consistency, so it's sufficent to set it to NF3REG all the time. */
803 FILE_FULL_EA_INFORMATION ffei;
804 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
806 ffei_buf.ffei.NextEntryOffset = 0;
807 ffei_buf.ffei.Flags = 0;
808 ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
809 ffei_buf.ffei.EaValueLength = sizeof (fattr3);
810 strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
811 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
812 + ffei_buf.ffei.EaNameLength + 1);
813 memset (nfs_attr, 0, sizeof (fattr3));
814 nfs_attr->type = NF3REG;
815 nfs_attr->mode = mode;
816 status = NtSetEaFile (get_handle (), &io,
817 &ffei_buf.ffei, sizeof ffei_buf);
818 if (!NT_SUCCESS (status))
819 __seterrno_from_nt_status (status);
829 if (!set_file_attribute (get_handle (), pc,
830 ILLEGAL_UID, ILLEGAL_GID, mode))
834 /* If the mode has any write bits set, the DOS R/O flag is in the way. */
835 if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
836 pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
837 else if (!pc.has_acls ()) /* Never set DOS R/O if security is used. */
838 pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
840 pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
842 status = NtSetAttributesFile (get_handle (), pc.file_attributes ());
843 /* MVFS needs a good amount of kicking to be convinced that it has to write
844 back metadata changes and to invalidate the cached metadata information
845 stored for the given handle. This method to open a second handle to
846 the file and write the same metadata information twice has been found
847 experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
848 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !oret)
850 OBJECT_ATTRIBUTES attr;
853 pc.init_reopen_attr (&attr, get_handle ());
854 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
855 FILE_SHARE_VALID_FLAGS,
856 FILE_OPEN_FOR_BACKUP_INTENT)))
858 NtSetAttributesFile (fh, pc.file_attributes ());
862 /* Correct NTFS security attributes have higher priority */
865 if (!NT_SUCCESS (status))
866 __seterrno_from_nt_status (status);
879 fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
885 /* fake - if not supported, pretend we're like win95
886 where it just works */
887 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
893 query_open (query_write_control);
894 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
902 int res = get_file_attribute (get_handle (), pc, &attrib, &old_uid, NULL);
905 /* Typical Windows default ACLs can contain permissions for one
906 group, while being owned by another user/group. The permission
907 bits returned above are pretty much useless then. Creating a
908 new ACL with these useless permissions results in a potentially
909 broken symlink. So what we do here is to set the underlying
910 permissions of symlinks to a sensible value which allows the
911 world to read the symlink and only the new owner to change it. */
913 attrib = S_IFLNK | STD_RBITS | STD_WBITS;
914 res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
915 /* If you're running a Samba server which has no winbidd running, the
916 uid<->SID mapping is disfunctional. Even trying to chown to your
917 own account fails since the account used on the server is the UNIX
918 account which gets used for the standard user mapping. This is a
919 default mechanism which doesn't know your real Windows SID.
920 There are two possible error codes in different Samba releases for
921 this situation, one of them is unfortunately the not very significant
922 STATUS_ACCESS_DENIED. Instead of relying on the error codes, we're
923 using the below very simple heuristic. If set_file_attribute failed,
924 and the original user account was either already unknown, or one of
925 the standard UNIX accounts, we're faking success. */
926 if (res == -1 && pc.fs_is_samba ())
930 if (old_uid == ILLEGAL_UID
931 || (sid.getfrompw (internal_getpwuid (old_uid))
932 && RtlEqualPrefixSid (sid,
933 well_known_samba_unix_user_fake_sid)))
935 debug_printf ("Faking chown worked on standalone Samba");
947 fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp)
960 /* Open for writing required to be able to set ctime
961 (even though setting the ACL is just pretended). */
963 oret = open (O_WRONLY | O_BINARY, 0);
969 else if (nentries < MIN_ACL_ENTRIES)
975 aclbufp[0].a_type = USER_OBJ;
976 aclbufp[0].a_id = st.st_uid;
977 aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
978 aclbufp[1].a_type = GROUP_OBJ;
979 aclbufp[1].a_id = st.st_gid;
980 aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
981 aclbufp[2].a_type = OTHER_OBJ;
982 aclbufp[2].a_id = ILLEGAL_GID;
983 aclbufp[2].a_perm = st.st_mode & S_IRWXO;
984 aclbufp[3].a_type = CLASS_OBJ;
985 aclbufp[3].a_id = ILLEGAL_GID;
986 aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
987 res = MIN_ACL_ENTRIES;
992 res = MIN_ACL_ENTRIES;
1001 if ((cmd == SETACL && !get_handle ())
1002 || (cmd != SETACL && !get_stat_handle ()))
1004 query_open (cmd == SETACL ? query_write_control : query_read_control);
1005 if (!(oret = open (O_BINARY, 0)))
1007 if (cmd == GETACL || cmd == GETACLCNT)
1008 goto cant_access_acl;
1015 if (!aclsort32 (nentries, 0, aclbufp))
1018 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1022 FILE_BASIC_INFORMATION fbi;
1023 fbi.CreationTime.QuadPart
1024 = fbi.LastAccessTime.QuadPart
1025 = fbi.LastWriteTime.QuadPart
1026 = fbi.ChangeTime.QuadPart = 0LL;
1027 fbi.FileAttributes = (pc.file_attributes ()
1028 & ~FILE_ATTRIBUTE_READONLY)
1029 ?: FILE_ATTRIBUTE_NORMAL;
1030 NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1031 FileBasicInformation);
1039 res = getacl (get_stat_handle (), pc, nentries, aclbufp);
1040 /* For this ENOSYS case, see security.cc:get_file_attribute(). */
1041 if (res == -1 && get_errno () == ENOSYS)
1042 goto cant_access_acl;
1045 res = getacl (get_stat_handle (), pc, 0, NULL);
1047 if (res == -1 && get_errno () == ENOSYS)
1048 goto cant_access_acl;
1063 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1065 if (pc.is_fs_special ())
1067 set_errno (ENOTSUP);
1070 return read_ea (get_handle (), pc, name, (char *) value, size);
1074 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1077 if (pc.is_fs_special ())
1079 set_errno (ENOTSUP);
1082 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1086 fhandler_disk_file::fadvise (_off64_t offset, _off64_t length, int advice)
1088 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1094 /* Windows only supports advice flags for the whole file. We're using
1095 a simplified test here so that we don't have to ask for the actual
1096 file size. Length == 0 means all bytes starting at offset anyway.
1097 So we only actually follow the advice, if it's given for offset == 0. */
1101 /* We only support normal and sequential mode for now. Everything which
1102 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1103 if (advice != POSIX_FADV_SEQUENTIAL)
1104 advice = POSIX_FADV_NORMAL;
1107 FILE_MODE_INFORMATION fmi;
1108 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1110 FileModeInformation);
1111 if (!NT_SUCCESS (status))
1112 __seterrno_from_nt_status (status);
1115 fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1116 if (advice == POSIX_FADV_SEQUENTIAL)
1117 fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1118 status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1119 FileModeInformation);
1120 if (NT_SUCCESS (status))
1122 __seterrno_from_nt_status (status);
1129 fhandler_disk_file::ftruncate (_off64_t length, bool allow_truncate)
1133 if (length < 0 || !get_handle ())
1135 else if (pc.isdir ())
1137 else if (!(get_access () & GENERIC_WRITE))
1143 FILE_STANDARD_INFORMATION fsi;
1144 FILE_END_OF_FILE_INFORMATION feofi;
1146 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1147 FileStandardInformation);
1148 if (!NT_SUCCESS (status))
1150 __seterrno_from_nt_status (status);
1154 /* If called through posix_fallocate, silently succeed if length
1155 is less than the file's actual length. */
1156 if (!allow_truncate && length < fsi.EndOfFile.QuadPart)
1159 feofi.EndOfFile.QuadPart = length;
1160 /* Create sparse files only when called through ftruncate, not when
1161 called through posix_fallocate. */
1163 && (pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES)
1164 && length >= fsi.EndOfFile.QuadPart + (128 * 1024))
1166 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1167 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1168 syscall_printf ("%p = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1169 status, pc.get_nt_native_path ());
1171 status = NtSetInformationFile (get_handle (), &io,
1172 &feofi, sizeof feofi,
1173 FileEndOfFileInformation);
1174 if (!NT_SUCCESS (status))
1175 __seterrno_from_nt_status (status);
1183 fhandler_disk_file::link (const char *newpath)
1185 size_t nlen = strlen (newpath);
1186 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
1189 set_errno (newpc.error);
1193 if (newpc.exists ())
1195 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1200 if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
1206 char new_buf[nlen + 5];
1209 /* If the original file is a lnk special file (except for sockets),
1210 and if the original file has a .lnk suffix, add one to the hardlink
1212 if (pc.is_lnk_special () && !pc.issocket ()
1213 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1216 /* Shortcut hack. */
1217 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1219 newpc.check (newpath, PC_SYM_NOFOLLOW);
1221 else if (!pc.isdir ()
1223 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1225 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1228 /* Executable hack. */
1229 stpcpy (stpcpy (new_buf, newpath), ".exe");
1231 newpc.check (newpath, PC_SYM_NOFOLLOW);
1235 /* We only need READ_CONTROL access so the handle returned in pc is
1236 sufficient. And if the file couldn't be opened with READ_CONTROL
1237 access in path_conv, we won't be able to do it here anyway. */
1238 HANDLE fh = get_stat_handle ();
1244 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1245 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1246 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1247 pfli->ReplaceIfExists = FALSE;
1248 pfli->RootDirectory = NULL;
1249 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1253 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1254 if (!NT_SUCCESS (status))
1256 if (status == STATUS_INVALID_DEVICE_REQUEST)
1258 /* FS doesn't support hard links. Linux returns EPERM. */
1264 __seterrno_from_nt_status (status);
1272 fhandler_disk_file::utimens (const struct timespec *tvp)
1274 return utimens_fs (tvp);
1278 fhandler_base::utimens_fs (const struct timespec *tvp)
1280 struct timespec timeofday;
1281 struct timespec tmp[2];
1282 bool closeit = false;
1286 query_open (query_write_attributes);
1287 if (!open_fs (O_BINARY, 0))
1289 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1290 to change the timestamps. Unfortunately it's not sufficient for a
1291 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1292 for writing, though this fails for R/O files of course. */
1293 query_open (no_query);
1294 if (!open_fs (O_WRONLY | O_BINARY, 0))
1296 syscall_printf ("Opening file failed");
1303 clock_gettime (CLOCK_REALTIME, &timeofday);
1305 tmp[1] = tmp[0] = timeofday;
1308 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1309 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1316 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1317 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1319 debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
1322 FILE_BASIC_INFORMATION fbi;
1324 fbi.CreationTime.QuadPart = 0LL;
1325 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1326 timespec_to_filetime (&tmp[0], (LPFILETIME) &fbi.LastAccessTime);
1327 timespec_to_filetime (&tmp[1], (LPFILETIME) &fbi.LastWriteTime);
1328 fbi.ChangeTime.QuadPart = 0LL;
1329 fbi.FileAttributes = 0;
1330 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1331 FileBasicInformation);
1332 /* For this special case for MVFS see the comment in
1333 fhandler_disk_file::fchmod. */
1334 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
1336 OBJECT_ATTRIBUTES attr;
1339 pc.init_reopen_attr (&attr, get_handle ());
1340 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
1341 FILE_SHARE_VALID_FLAGS,
1342 FILE_OPEN_FOR_BACKUP_INTENT)))
1344 NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1345 FileBasicInformation);
1351 /* Opening a directory on a 9x share from a NT machine works(!), but
1352 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1353 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1355 __seterrno_from_nt_status (status);
1361 fhandler_disk_file::fhandler_disk_file () :
1362 fhandler_base (), prw_handle (NULL)
1366 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1367 fhandler_base (), prw_handle (NULL)
1373 fhandler_disk_file::open (int flags, mode_t mode)
1375 return open_fs (flags, mode);
1379 fhandler_disk_file::close ()
1381 /* Close extra pread/pwrite handle, if it exists. */
1383 NtClose (prw_handle);
1384 /* Delete all POSIX locks on the file. Delete all flock locks on the
1385 file if this is the last reference to this file. */
1386 del_my_locks (on_close);
1387 return fhandler_base::close ();
1391 fhandler_disk_file::dup (fhandler_base *child, int flags)
1393 fhandler_disk_file *fhc = (fhandler_disk_file *) child;
1395 int ret = fhandler_base::dup (child, flags);
1396 if (!ret && prw_handle
1397 && !DuplicateHandle (GetCurrentProcess (), prw_handle,
1398 GetCurrentProcess (), &fhc->prw_handle,
1399 0, TRUE, DUPLICATE_SAME_ACCESS))
1405 fhandler_disk_file::fixup_after_fork (HANDLE parent)
1408 fhandler_base::fixup_after_fork (parent);
1412 fhandler_base::open_fs (int flags, mode_t mode)
1414 /* Unfortunately NT allows to open directories for writing, but that's
1415 disallowed according to SUSv3. */
1416 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1422 int res = fhandler_base::open (flags | O_DIROPEN, mode);
1426 /* This is for file systems known for having a buggy CreateFile call
1427 which might return a valid HANDLE without having actually opened
1429 The only known file system to date is the SUN NFS Solstice Client 3.1
1430 which returns a valid handle when trying to open a file in a nonexistent
1432 if (pc.has_buggy_open () && !pc.exists ())
1434 debug_printf ("Buggy open detected.");
1440 ino = pc.get_ino_by_handle (get_handle ());
1441 /* A unique ID is necessary to recognize fhandler entries which are
1442 duplicated by dup(2) or fork(2). */
1443 NtAllocateLocallyUniqueId ((PLUID) &unique_id);
1446 syscall_printf ("%d = fhandler_disk_file::open(%S, %p)", res,
1447 pc.get_nt_native_path (), flags);
1451 /* POSIX demands that pread/pwrite don't change the current file position.
1452 While NtReadFile/NtWriteFile support atomic seek-and-io, both change the
1453 file pointer if the file handle has been opened for synchonous I/O.
1454 Using this handle for pread/pwrite would break atomicity, because the
1455 read/write operation would have to be followed by a seek back to the old
1456 file position. What we do is to open another handle to the file on the
1457 first call to either pread or pwrite. This is used for any subsequent
1458 pread/pwrite. Thus the current file position of the "normal" file
1459 handle is not touched.
1463 Note that this is just a hack. The problem with this approach is that
1464 a change to the file permissions might disallow to open the file with
1465 the required permissions to read or write. This appears to be a border case,
1466 but that's exactly what git does. It creates the file for reading and
1467 writing and after writing it, it chmods the file to read-only. Then it
1468 calls pread on the file to examine the content. This works, but if git
1469 would use the original handle to pwrite to the file, it would be broken
1472 One way to implement this is to open the pread/pwrite handle right at
1473 file open time. We would simply maintain two handles, which wouldn't
1474 be much of a problem given how we do that for other fhandler types as
1477 However, ultimately fhandler_disk_file should become a derived class of
1478 fhandler_base_overlapped. Each raw_read or raw_write would fetch the
1479 actual file position, read/write from there, and then set the file
1480 position again. Fortunately, while the file position is not maintained
1481 by the I/O manager, it can be fetched and set to a new value by all
1482 processes holding a handle to that file object. Pread and pwrite differ
1483 from raw_read and raw_write just by not touching the current file pos.
1484 Actually they could be merged with raw_read/raw_write if we add a position
1485 parameter to the latter. */
1488 fhandler_disk_file::prw_open (bool write)
1492 OBJECT_ATTRIBUTES attr;
1494 /* First try to open with the original access mask */
1495 ACCESS_MASK access = get_access ();
1496 pc.init_reopen_attr (&attr, get_handle ());
1497 status = NtOpenFile (&prw_handle, access, &attr, &io,
1498 FILE_SHARE_VALID_FLAGS, get_options ());
1499 if (status == STATUS_ACCESS_DENIED)
1501 /* If we get an access denied, chmod has been called. Try again
1502 with just the required rights to perform the called function. */
1503 access &= write ? ~GENERIC_READ : ~GENERIC_WRITE;
1504 status = NtOpenFile (&prw_handle, access, &attr, &io,
1505 FILE_SHARE_VALID_FLAGS, get_options ());
1507 debug_printf ("%x = NtOpenFile (%p, %x, %S, io, %x, %x)",
1508 status, prw_handle, access, pc.get_nt_native_path (),
1509 FILE_SHARE_VALID_FLAGS, get_options ());
1510 if (!NT_SUCCESS (status))
1512 __seterrno_from_nt_status (status);
1519 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
1521 if ((get_flags () & O_ACCMODE) == O_WRONLY)
1527 /* In binary mode, we can use an atomic NtReadFile call. */
1530 extern int __stdcall is_at_eof (HANDLE h);
1533 LARGE_INTEGER off = { QuadPart:offset };
1535 if (!prw_handle && prw_open (false))
1537 status = NtReadFile (prw_handle, NULL, NULL, NULL, &io, buf, count,
1539 if (!NT_SUCCESS (status))
1546 if (status == (NTSTATUS) STATUS_ACCESS_VIOLATION)
1548 if (is_at_eof (prw_handle))
1550 switch (mmap_is_attached_or_noreserve (buf, count))
1552 case MMAP_NORESERVE_COMMITED:
1553 status = NtReadFile (prw_handle, NULL, NULL, NULL, &io,
1554 buf, count, &off, NULL);
1555 if (NT_SUCCESS (status))
1556 return io.Information;
1558 case MMAP_RAISE_SIGBUS:
1564 __seterrno_from_nt_status (status);
1570 /* Text mode stays slow and non-atomic. */
1572 _off64_t curpos = lseek (0, SEEK_CUR);
1573 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1577 size_t tmp_count = count;
1578 read (buf, tmp_count);
1579 if (lseek (curpos, SEEK_SET) >= 0)
1580 res = (ssize_t) tmp_count;
1584 debug_printf ("%d = pread(%p, %d, %d)\n", res, buf, count, offset);
1589 fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
1591 if ((get_flags () & O_ACCMODE) == O_RDONLY)
1597 /* In binary mode, we can use an atomic NtWriteFile call. */
1602 LARGE_INTEGER off = { QuadPart:offset };
1604 if (!prw_handle && prw_open (true))
1606 status = NtWriteFile (prw_handle, NULL, NULL, NULL, &io, buf, count,
1608 if (!NT_SUCCESS (status))
1610 __seterrno_from_nt_status (status);
1613 return io.Information;
1617 /* Text mode stays slow and non-atomic. */
1619 _off64_t curpos = lseek (0, SEEK_CUR);
1620 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1624 res = (ssize_t) write (buf, count);
1625 if (lseek (curpos, SEEK_SET) < 0)
1628 debug_printf ("%d = pwrite(%p, %d, %d)\n", res, buf, count, offset);
1633 fhandler_disk_file::mkdir (mode_t mode)
1636 SECURITY_ATTRIBUTES sa = sec_none_nih;
1639 OBJECT_ATTRIBUTES attr;
1641 PFILE_FULL_EA_INFORMATION p = NULL;
1643 ULONG access = FILE_LIST_DIRECTORY | SYNCHRONIZE;
1645 if (pc.fs_is_nfs ())
1647 /* When creating a dir on an NFS share, we have to set the
1648 file mode by writing a NFS fattr3 structure with the
1649 correct mode bits set. */
1650 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1652 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1653 p->NextEntryOffset = 0;
1655 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1656 p->EaValueLength = sizeof (fattr3);
1657 strcpy (p->EaName, NFS_V3_ATTR);
1658 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1659 memset (nfs_attr, 0, sizeof (fattr3));
1660 nfs_attr->type = NF3DIR;
1661 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1663 else if (has_acls () && !isremote ())
1664 /* If the filesystem supports ACLs, we will overwrite the DACL after the
1665 call to NtCreateFile. This requires a handle with READ_CONTROL and
1666 WRITE_DAC access, otherwise get_file_sd and set_file_sd both have to
1667 open the file again.
1668 FIXME: On remote NTFS shares open sometimes fails because even the
1669 creator of the file doesn't have the right to change the DACL.
1670 I don't know what setting that is or how to recognize such a share,
1671 so for now we don't request WRITE_DAC on remote drives. */
1672 access |= READ_CONTROL | WRITE_DAC;
1673 status = NtCreateFile (&dir, access, pc.get_object_attr (attr, sa), &io, NULL,
1674 FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
1676 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1677 | FILE_OPEN_FOR_BACKUP_INTENT,
1679 if (NT_SUCCESS (status))
1682 set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
1683 S_JUSTCREATED | S_IFDIR
1684 | ((mode & 07777) & ~cygheap->umask));
1689 __seterrno_from_nt_status (status);
1695 fhandler_disk_file::rmdir ()
1697 extern NTSTATUS unlink_nt (path_conv &pc);
1701 set_errno (ENOTDIR);
1710 NTSTATUS status = unlink_nt (pc);
1712 /* Check for existence of remote dirs after trying to delete them.
1714 - Sometimes SMB indicates failure when it really succeeds.
1715 - Removing a directory on a Samba drive using an old Samba version
1716 sometimes doesn't return an error, if the directory can't be removed
1717 because it's not empty. */
1720 OBJECT_ATTRIBUTES attr;
1721 FILE_BASIC_INFORMATION fbi;
1724 q_status = NtQueryAttributesFile (pc.get_object_attr (attr, sec_none_nih),
1726 if (!NT_SUCCESS (status) && q_status == STATUS_OBJECT_NAME_NOT_FOUND)
1727 status = STATUS_SUCCESS;
1728 else if (pc.fs_is_samba ()
1729 && NT_SUCCESS (status) && NT_SUCCESS (q_status))
1730 status = STATUS_DIRECTORY_NOT_EMPTY;
1732 if (!NT_SUCCESS (status))
1734 __seterrno_from_nt_status (status);
1740 /* This is the minimal number of entries which fit into the readdir cache.
1741 The number of bytes allocated by the cache is determined by this number,
1742 To tune caching, just tweak this number. To get a feeling for the size,
1743 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1745 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1747 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1748 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1749 + (NAME_MAX + 1) * sizeof (WCHAR)))
1753 char __cache[DIR_BUF_SIZE]; /* W2K needs this buffer 8 byte aligned. */
1757 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1758 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1760 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1763 fhandler_disk_file::opendir (int fd)
1769 set_errno (ENOTDIR);
1770 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1772 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1778 else if ((dir->__d_dirent =
1779 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1787 if (cfd < 0 && fd < 0)
1790 dir->__d_dirent->__d_version = __DIRENT_VERSION;
1791 dir->__d_cookie = __DIRENT_COOKIE;
1792 dir->__handle = INVALID_HANDLE_VALUE;
1793 dir->__d_position = 0;
1794 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1795 ? dirent_isroot : 0;
1796 dir->__d_internal = (unsigned) new __DIR_mounts (get_name ());
1797 d_cachepos (dir) = 0;
1799 if (!pc.iscygdrive ())
1803 /* opendir() case. Initialize with given directory name and
1804 NULL directory handle. */
1805 OBJECT_ATTRIBUTES attr;
1808 /* Tools like ls(1) call dirfd() to fetch the directory
1809 descriptor for calls to facl or fstat. The tight access mask
1810 used so far is not sufficient to reuse the handle for these
1811 calls, instead the facl/fstat calls find the handle to be
1812 unusable and have to re-open the file for reading attributes
1813 and control data. So, what we do here is to try to open the
1814 directory with more relaxed access mask which enables to use
1815 the handle for the aforementioned purpose. This should work
1816 in almost all cases. Only if it doesn't work due to
1817 permission problems, we drop the additional access bits and
1819 ACCESS_MASK fstat_mask = READ_CONTROL | FILE_READ_ATTRIBUTES;
1823 status = NtOpenFile (&get_handle (),
1824 SYNCHRONIZE | FILE_LIST_DIRECTORY
1826 pc.get_object_attr (attr, sec_none_nih),
1827 &io, FILE_SHARE_VALID_FLAGS,
1828 FILE_SYNCHRONOUS_IO_NONALERT
1829 | FILE_OPEN_FOR_BACKUP_INTENT
1830 | FILE_DIRECTORY_FILE);
1831 if (!NT_SUCCESS (status))
1833 if (status == STATUS_ACCESS_DENIED && fstat_mask)
1837 __seterrno_from_nt_status (status);
1842 while (!NT_SUCCESS (status));
1845 /* FileIdBothDirectoryInformation is apparently unsupported on
1846 XP when accessing directories on UDF. When trying to use it
1847 so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1848 It's not clear if the call isn't also unsupported on other
1849 OS/FS combinations (say, Win2K/CDFS or so). Instead of
1850 testing in readdir for yet another error code, let's use
1851 FileIdBothDirectoryInformation only on filesystems supporting
1852 persistent ACLs, FileBothDirectoryInformation otherwise.
1854 NFS clients hide dangling symlinks from directory queries,
1855 unless you use the FileNamesInformation info class.
1856 On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
1857 works fine, but only if the NFS share is mounted to a drive
1858 letter. TODO: We don't test that here for now, but it might
1859 be worth to test if there's a speed gain in using
1860 FileIdBothDirectoryInformation, because it doesn't require to
1861 open the file to read the inode number. */
1862 if (pc.hasgood_inode ())
1864 dir->__flags |= dirent_set_d_ino;
1865 if (pc.fs_is_nfs ())
1866 dir->__flags |= dirent_nfs_d_ino;
1867 else if (!pc.has_buggy_fileid_dirinfo ())
1868 dir->__flags |= dirent_get_d_ino;
1875 /* Filling cfd with `this' (aka storing this in the file
1876 descriptor table should only happen after it's clear that
1877 opendir doesn't fail, otherwise we end up cfree'ing the
1878 fhandler twice, once in opendir() in dir.cc, the second
1879 time on exit. Nasty, nasty... */
1882 if (pc.iscygdrive ())
1883 cfd->nohandle (true);
1885 set_close_on_exec (true);
1890 syscall_printf ("%p = opendir (%s)", res, get_name ());
1894 delete d_mounts (dir);
1896 free (dir->__d_dirent);
1898 free (dir->__d_dirname);
1905 readdir_get_ino (const char *path, bool dot_dot)
1910 OBJECT_ATTRIBUTES attr;
1916 fname = (char *) alloca (strlen (path) + 4);
1917 char *c = stpcpy (fname, path);
1923 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN | PC_KEEP_HANDLE);
1924 if (pc.isspecial ())
1926 if (!stat_worker (pc, &st))
1929 else if (!pc.hasgood_inode ())
1930 ino = hash_path_name (0, pc.get_nt_native_path ());
1931 else if ((hdl = pc.handle ()) != NULL
1932 || NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1933 pc.get_object_attr (attr, sec_none_nih),
1934 &io, FILE_SHARE_VALID_FLAGS,
1935 FILE_OPEN_FOR_BACKUP_INTENT
1936 | (pc.is_rep_symlink ()
1937 ? FILE_OPEN_REPARSE_POINT : 0)))
1940 ino = pc.get_ino_by_handle (hdl);
1942 ino = hash_path_name (0, pc.get_nt_native_path ());
1948 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1949 DWORD attr, PUNICODE_STRING fname)
1954 if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
1959 return geterrno_from_win_error (w32_err);
1963 dir->__flags &= ~dirent_set_d_ino;
1966 /* Set d_type if type can be determined from file attributes. For .lnk
1967 symlinks, d_type will be reset below. Reparse points can be NTFS
1968 symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
1970 !(attr & (~FILE_ATTRIBUTE_VALID_FLAGS | FILE_ATTRIBUTE_REPARSE_POINT)))
1972 if (attr & FILE_ATTRIBUTE_DIRECTORY)
1973 de->d_type = DT_DIR;
1974 /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
1975 else if (!(attr & FILE_ATTRIBUTE_SYSTEM))
1976 de->d_type = DT_REG;
1979 /* Check for directory reparse point. These are potential volume mount
1980 points which have another inode than the underlying directory. */
1981 if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1982 == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1985 OBJECT_ATTRIBUTES attr;
1988 InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
1989 get_handle (), NULL);
1990 de->d_type = readdir_check_reparse_point (&attr);
1991 if (de->d_type == DT_DIR)
1993 /* Volume mountpoints are treated as directories. We have to fix
1994 the inode number, otherwise we have the inode number of the
1995 mount point, rather than the inode number of the toplevel
1996 directory of the mounted drive. */
1997 if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
1998 FILE_SHARE_VALID_FLAGS,
1999 FILE_OPEN_FOR_BACKUP_INTENT)))
2001 de->d_ino = pc.get_ino_by_handle (reph);
2007 /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
2008 .lnk suffix and set d_type accordingly. */
2009 if ((attr & (FILE_ATTRIBUTE_DIRECTORY
2010 | FILE_ATTRIBUTE_REPARSE_POINT
2011 | FILE_ATTRIBUTE_READONLY)) == FILE_ATTRIBUTE_READONLY
2012 && fname->Length > 4 * sizeof (WCHAR))
2014 UNICODE_STRING uname;
2016 RtlInitCountedUnicodeString (&uname,
2018 + fname->Length / sizeof (WCHAR) - 4,
2019 4 * sizeof (WCHAR));
2020 if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
2023 char *file = tp.c_get ();
2024 char *p = stpcpy (file, pc.normalized_path);
2027 sys_wcstombs (p, NT_MAX_PATH - (p - file),
2028 fname->Buffer, fname->Length / sizeof (WCHAR));
2029 path_conv fpath (file, PC_SYM_NOFOLLOW);
2030 if (fpath.issymlink ())
2032 fname->Length -= 4 * sizeof (WCHAR);
2033 de->d_type = DT_LNK;
2035 else if (fpath.isfifo ())
2037 fname->Length -= 4 * sizeof (WCHAR);
2038 de->d_type = DT_FIFO;
2040 else if (fpath.is_fs_special ())
2042 fname->Length -= 4 * sizeof (WCHAR);
2043 de->d_type = S_ISCHR (fpath.dev.mode) ? DT_CHR : DT_BLK;
2048 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
2049 fname->Length / sizeof (WCHAR));
2051 /* Don't try to optimize relative to dir->__d_position. On several
2052 filesystems it's no safe bet that "." and ".." entries always
2054 if (de->d_name[0] == '.')
2056 if (de->d_name[1] == '\0')
2057 dir->__flags |= dirent_saw_dot;
2058 else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
2059 dir->__flags |= dirent_saw_dot_dot;
2065 fhandler_disk_file::readdir (DIR *dir, dirent *de)
2068 NTSTATUS status = STATUS_SUCCESS;
2069 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
2071 ULONG FileNameLength;
2072 ULONG FileAttributes = 0;
2074 UNICODE_STRING fname;
2076 /* d_cachepos always refers to the next cache entry to use. If it's 0
2077 we must reload the cache. */
2078 if (d_cachepos (dir) == 0)
2080 if ((dir->__flags & dirent_get_d_ino))
2082 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2083 d_cache (dir), DIR_BUF_SIZE,
2084 FileIdBothDirectoryInformation,
2085 FALSE, NULL, dir->__d_position == 0);
2086 /* FileIdBothDirectoryInformation isn't supported for remote drives
2087 on NT4 and 2K systems, and it's also not supported on 2K at all,
2088 when accessing network drives on any remote OS. There are also
2089 hacked versions of Samba 3.0.x out there (Debian-based it seems),
2090 which return STATUS_NOT_SUPPORTED rather than handling this info
2091 class. We just fall back to using a standard directory query in
2092 this case and note this case using the dirent_get_d_ino flag. */
2093 if (!NT_SUCCESS (status) && status != STATUS_NO_MORE_FILES
2094 && (status == STATUS_INVALID_LEVEL
2095 || status == STATUS_NOT_SUPPORTED
2096 || status == STATUS_INVALID_PARAMETER
2097 || status == STATUS_INVALID_NETWORK_RESPONSE
2098 || status == STATUS_INVALID_INFO_CLASS))
2099 dir->__flags &= ~dirent_get_d_ino;
2100 /* Something weird happens on Samba up to version 3.0.21c, which is
2101 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
2102 nicely, but only up to the 128th entry in the directory. After
2103 reaching this entry, the next call to NtQueryDirectoryFile
2104 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
2105 Why should we care, we can just switch to
2106 FileBothDirectoryInformation, isn't it? Nope! The next call to
2107 NtQueryDirectoryFile(FileBothDirectoryInformation) actually
2108 returns STATUS_NO_MORE_FILES, regardless how many files are left
2109 unread in the directory. This does not happen when using
2110 FileBothDirectoryInformation right from the start, but since
2111 we can't decide whether the server we're talking with has this
2112 bug or not, we end up serving Samba shares always in the slow
2113 mode using FileBothDirectoryInformation. So, what we do here is
2114 to implement the solution suggested by Andrew Tridgell, we just
2115 reread all entries up to dir->d_position using
2116 FileBothDirectoryInformation.
2117 However, We do *not* mark this server as broken and fall back to
2118 using FileBothDirectoryInformation further on. This would slow
2119 down every access to such a server, even for directories under
2120 128 entries. Also, bigger dirs only suffer from one additional
2121 call per full directory scan, which shouldn't be too big a hit.
2122 This can easily be changed if necessary. */
2123 if (status == STATUS_INVALID_LEVEL && dir->__d_position)
2125 d_cachepos (dir) = 0;
2126 for (int cnt = 0; cnt < dir->__d_position; ++cnt)
2128 if (d_cachepos (dir) == 0)
2130 status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
2131 NULL, &io, d_cache (dir),
2133 FileBothDirectoryInformation,
2134 FALSE, NULL, cnt == 0);
2135 if (!NT_SUCCESS (status))
2138 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
2139 + d_cachepos (dir));
2140 if (buf->NextEntryOffset == 0)
2141 d_cachepos (dir) = 0;
2143 d_cachepos (dir) += buf->NextEntryOffset;
2148 if (!(dir->__flags & dirent_get_d_ino))
2149 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
2150 d_cache (dir), DIR_BUF_SIZE,
2151 (dir->__flags & dirent_nfs_d_ino)
2152 ? FileNamesInformation
2153 : FileBothDirectoryInformation,
2154 FALSE, NULL, dir->__d_position == 0);
2159 if (status == STATUS_NO_MORE_FILES)
2161 else if (!NT_SUCCESS (status))
2162 debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
2163 status, RtlNtStatusToDosError (status));
2166 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
2167 if (buf->NextEntryOffset == 0)
2168 d_cachepos (dir) = 0;
2170 d_cachepos (dir) += buf->NextEntryOffset;
2171 if ((dir->__flags & dirent_get_d_ino))
2173 FileName = buf->FileName;
2174 FileNameLength = buf->FileNameLength;
2175 FileAttributes = buf->FileAttributes;
2176 if ((dir->__flags & dirent_set_d_ino))
2177 de->d_ino = buf->FileId.QuadPart;
2179 else if ((dir->__flags & dirent_nfs_d_ino))
2181 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
2182 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
2186 FileName = ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileName;
2188 ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileNameLength;
2190 ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileAttributes;
2192 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
2193 de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
2194 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
2196 /* Don't try to optimize relative to dir->__d_position. On several
2197 filesystems it's no safe bet that "." and ".." entries always
2199 if (FileNameLength == sizeof (WCHAR) && FileName[0] == '.')
2200 de->d_ino = pc.get_ino_by_handle (get_handle ());
2201 else if (FileNameLength == 2 * sizeof (WCHAR)
2202 && FileName[0] == L'.' && FileName[1] == L'.')
2204 if (!(dir->__flags & dirent_isroot))
2205 de->d_ino = readdir_get_ino (get_name (), true);
2207 de->d_ino = pc.get_ino_by_handle (get_handle ());
2211 OBJECT_ATTRIBUTES attr;
2215 InitializeObjectAttributes (&attr, &fname,
2216 pc.objcaseinsensitive (),
2217 get_handle (), NULL);
2218 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2219 NtOpenFile here returns the inode number of the symlink target,
2220 rather than the inode number of the symlink itself.
2222 Worse, trying to open a symlink without setting the special
2223 "ActOnSymlink" EA triggers a bug in Windows 7 which results
2224 in a timeout of up to 20 seconds, followed by two exceptions
2227 Since both results are far from desirable, we open symlinks
2228 on NFS so that we get the right inode and a happy W7.
2229 And, since some filesystems choke on the EAs, we don't
2230 use them unconditionally. */
2231 f_status = (dir->__flags & dirent_nfs_d_ino)
2232 ? NtCreateFile (&hdl, READ_CONTROL, &attr, &io,
2233 NULL, 0, FILE_SHARE_VALID_FLAGS,
2234 FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT,
2235 &nfs_aol_ffei, sizeof nfs_aol_ffei)
2236 : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
2237 FILE_SHARE_VALID_FLAGS,
2238 FILE_OPEN_FOR_BACKUP_INTENT
2239 | FILE_OPEN_REPARSE_POINT);
2240 if (NT_SUCCESS (f_status))
2242 /* We call NtQueryInformationFile here, rather than
2243 pc.get_ino_by_handle(), otherwise we can't short-circuit
2244 dirent_set_d_ino correctly. */
2245 FILE_INTERNAL_INFORMATION fai;
2246 f_status = NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
2247 FileInternalInformation);
2249 if (NT_SUCCESS (f_status))
2251 if (pc.isgood_inode (fai.FileId.QuadPart))
2252 de->d_ino = fai.FileId.QuadPart;
2254 /* Untrusted file system. Don't try to fetch inode
2256 dir->__flags &= ~dirent_set_d_ino;
2263 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
2264 FileAttributes, &fname)))
2265 dir->__d_position++;
2266 else if (!(dir->__flags & dirent_saw_dot))
2268 strcpy (de->d_name , ".");
2269 de->d_ino = pc.get_ino_by_handle (get_handle ());
2270 de->d_type = DT_DIR;
2271 dir->__d_position++;
2272 dir->__flags |= dirent_saw_dot;
2275 else if (!(dir->__flags & dirent_saw_dot_dot))
2277 strcpy (de->d_name , "..");
2278 if (!(dir->__flags & dirent_isroot))
2279 de->d_ino = readdir_get_ino (get_name (), true);
2281 de->d_ino = pc.get_ino_by_handle (get_handle ());
2282 de->d_type = DT_DIR;
2283 dir->__d_position++;
2284 dir->__flags |= dirent_saw_dot_dot;
2288 syscall_printf ("%d = readdir(%p, %p) (L\"%lS\" > \"%ls\") (attr %p > type %d)",
2289 res, dir, &de, res ? NULL : &fname, res ? "***" : de->d_name,
2290 FileAttributes, de->d_type);
2295 fhandler_disk_file::telldir (DIR *dir)
2297 return dir->__d_position;
2301 fhandler_disk_file::seekdir (DIR *dir, long loc)
2304 while (loc > dir->__d_position)
2305 if (!::readdir (dir))
2310 fhandler_disk_file::rewinddir (DIR *dir)
2312 d_cachepos (dir) = 0;
2313 if (wincap.has_buggy_restart_scan () && isremote ())
2315 /* This works around a W2K bug. The RestartScan parameter in calls
2316 to NtQueryDirectoryFile on remote shares is ignored, thus
2317 resulting in not being able to rewind on remote shares. By
2318 reopening the directory, we get a fresh new directory pointer. */
2319 OBJECT_ATTRIBUTES attr;
2324 pc.init_reopen_attr (&attr, get_handle ());
2325 status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2326 &attr, &io, FILE_SHARE_VALID_FLAGS,
2327 FILE_SYNCHRONOUS_IO_NONALERT
2328 | FILE_OPEN_FOR_BACKUP_INTENT
2329 | FILE_DIRECTORY_FILE);
2330 if (!NT_SUCCESS (stat))
2331 debug_printf ("Unable to reopen dir %s, NT error: %p",
2332 get_name (), status);
2335 NtClose (get_handle ());
2336 set_io_handle (new_dir);
2339 dir->__d_position = 0;
2340 d_mounts (dir)->rewind ();
2344 fhandler_disk_file::closedir (DIR *dir)
2349 delete d_mounts (dir);
2352 else if (get_handle () == INVALID_HANDLE_VALUE)
2357 else if (!NT_SUCCESS (status = NtClose (get_handle ())))
2359 __seterrno_from_nt_status (status);
2362 syscall_printf ("%d = closedir(%p, %s)", res, dir, get_name ());
2366 fhandler_cygdrive::fhandler_cygdrive () :
2367 fhandler_disk_file (), ndrives (0), pdrive (NULL)
2372 fhandler_cygdrive::open (int flags, mode_t mode)
2374 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2379 if (flags & O_WRONLY)
2391 fhandler_cygdrive::close ()
2397 fhandler_cygdrive::set_drives ()
2399 pdrive = pdrive_buf;
2400 ndrives = GetLogicalDriveStrings (sizeof pdrive_buf, pdrive_buf) / DRVSZ;
2404 fhandler_cygdrive::fstat (struct __stat64 *buf)
2406 fhandler_base::fstat (buf);
2408 buf->st_mode = S_IFDIR | STD_RBITS | STD_XBITS;
2411 char flptst[] = "X:";
2413 for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
2414 if (is_floppy ((flptst[0] = *p, flptst))
2415 || GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
2417 buf->st_nlink = n + 2;
2422 fhandler_cygdrive::opendir (int fd)
2426 dir = fhandler_disk_file::opendir (fd);
2427 if (dir && !ndrives)
2434 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2436 char flptst[] = "X:";
2440 if (!pdrive || !*pdrive)
2442 if (!(dir->__flags & dirent_saw_dot))
2444 de->d_name[0] = '.';
2445 de->d_name[1] = '\0';
2450 if (!is_floppy ((flptst[0] = *pdrive, flptst))
2451 && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2453 pdrive = strchr (pdrive, '\0') + 1;
2455 *de->d_name = cyg_tolower (*pdrive);
2456 de->d_name[1] = '\0';
2457 user_shared->warned_msdos = true;
2458 de->d_ino = readdir_get_ino (pdrive, false);
2459 dir->__d_position++;
2460 pdrive = strchr (pdrive, '\0') + 1;
2461 syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2466 fhandler_cygdrive::rewinddir (DIR *dir)
2468 pdrive = pdrive_buf;
2469 dir->__d_position = 0;
2473 fhandler_cygdrive::closedir (DIR *dir)
2475 pdrive = pdrive_buf;