1 /* fhandler_disk_file.cc
3 Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 2005, 2006, 2007, 2008, 2009, 2010 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);
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 all remote inode numbers when running under NT4, as well as remote NT4
144 NTFS, as well as shares of Samba version < 3.0.
145 The known exception are SFU NFS shares, which return the valid 32 bit
146 inode number from the remote file system unchanged. */
147 return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ());
150 /* Check reparse point for type. IO_REPARSE_TAG_MOUNT_POINT types are
151 either volume mount points, which are treated as directories, or they
152 are directory mount points, which are treated as symlinks.
153 IO_REPARSE_TAG_SYMLINK types are always symlinks. We don't know
154 anything about other reparse points, so they are treated as unknown. */
156 readdir_check_reparse_point (POBJECT_ATTRIBUTES attr)
158 DWORD ret = DT_UNKNOWN;
161 UNICODE_STRING subst;
163 if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, attr, &io,
164 FILE_SHARE_VALID_FLAGS,
165 FILE_OPEN_FOR_BACKUP_INTENT
166 | FILE_OPEN_REPARSE_POINT)))
168 PREPARSE_DATA_BUFFER rp = (PREPARSE_DATA_BUFFER)
169 alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
170 if (NT_SUCCESS (NtFsControlFile (reph, NULL, NULL, NULL,
171 &io, FSCTL_GET_REPARSE_POINT, NULL, 0,
172 (LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE)))
174 if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
176 RtlInitCountedUnicodeString (&subst,
177 (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
178 + rp->MountPointReparseBuffer.SubstituteNameOffset),
179 rp->MountPointReparseBuffer.SubstituteNameLength);
180 /* Only volume mountpoints are treated as directories. */
181 if (RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))
186 else if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
195 path_conv::get_ino_by_handle (HANDLE hdl)
198 FILE_INTERNAL_INFORMATION fai;
200 if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
201 FileInternalInformation))
202 && isgood_inode (fai.FileId.QuadPart))
203 return fai.FileId.QuadPart;
208 /* This function is obsolete. We're keeping it in so we don't forget
209 that we already did all that at one point. */
211 path_conv::ndisk_links (DWORD nNumberOfLinks)
213 if (!isdir () || isremote ())
214 return nNumberOfLinks;
216 OBJECT_ATTRIBUTES attr;
220 if (!NT_SUCCESS (NtOpenFile (&fh, SYNCHRONIZE | FILE_LIST_DIRECTORY,
221 get_object_attr (attr, sec_none_nih),
222 &io, FILE_SHARE_VALID_FLAGS,
223 FILE_SYNCHRONOUS_IO_NONALERT
224 | FILE_OPEN_FOR_BACKUP_INTENT
225 | FILE_DIRECTORY_FILE)))
226 return nNumberOfLinks;
230 PFILE_BOTH_DIRECTORY_INFORMATION fdibuf = (PFILE_BOTH_DIRECTORY_INFORMATION)
232 __DIR_mounts *dir = new __DIR_mounts (normalized_path);
233 while (NT_SUCCESS (NtQueryDirectoryFile (fh, NULL, NULL, NULL, &io, fdibuf,
234 65536, FileBothDirectoryInformation,
235 FALSE, NULL, first)))
240 /* All directories have . and .. as their first entries.
241 If . is not present as first entry, we're on a drive's
242 root direcotry, which doesn't have these entries. */
243 if (fdibuf->FileNameLength != 2 || fdibuf->FileName[0] != L'.')
246 for (PFILE_BOTH_DIRECTORY_INFORMATION pfdi = fdibuf;
248 pfdi = (PFILE_BOTH_DIRECTORY_INFORMATION)
249 (pfdi->NextEntryOffset ? (PBYTE) pfdi + pfdi->NextEntryOffset
252 switch (pfdi->FileAttributes
253 & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
255 case FILE_ATTRIBUTE_DIRECTORY:
256 /* Just a directory */
259 case FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT:
260 /* Volume mount point or symlink to directory */
262 UNICODE_STRING fname;
264 RtlInitCountedUnicodeString (&fname, pfdi->FileName,
265 pfdi->FileNameLength);
266 InitializeObjectAttributes (&attr, &fname,
267 objcaseinsensitive (), fh, NULL);
268 if (is_volume_mountpoint (&attr))
275 UNICODE_STRING fname;
276 RtlInitCountedUnicodeString (&fname, pfdi->FileName,
277 pfdi->FileNameLength);
278 dir->check_mount (&fname, 0, false);
281 while (dir->check_missing_mount ())
289 /* For files on NFS shares, we request an EA of type NfsV3Attributes.
290 This returns the content of a struct fattr3 as defined in RFC 1813.
291 The content is the NFS equivalent of struct stat. so there's not much
292 to do here except for copying. */
294 fhandler_base::fstat_by_nfs_ea (struct __stat64 *buf)
296 fattr3 *nfs_attr = pc.nfsattr ();
298 if (get_io_handle ())
300 /* NFS stumbles over its own caching. If you write to the file,
301 a subsequent fstat does not return the actual size of the file,
302 but the size at the time the handle has been opened. Unless
303 access through another handle invalidates the caching within the
305 if (get_access () & GENERIC_WRITE)
306 FlushFileBuffers (get_io_handle ());
307 nfs_fetch_fattr3 (get_io_handle (), nfs_attr);
309 buf->st_dev = nfs_attr->fsid;
310 buf->st_ino = nfs_attr->fileid;
311 buf->st_mode = (nfs_attr->mode & 0xfff)
312 | nfs_type_mapping[nfs_attr->type & 7];
313 buf->st_nlink = nfs_attr->nlink;
314 /* FIXME: How to convert UNIX uid/gid to Windows SIDs? */
316 buf->st_uid = nfs_attr->uid;
317 buf->st_gid = nfs_attr->gid;
319 buf->st_uid = myself->uid;
320 buf->st_gid = myself->gid;
322 buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
323 nfs_attr->rdev.specdata2);
324 buf->st_size = nfs_attr->size;
325 buf->st_blksize = PREFERRED_IO_BLKSIZE;
326 buf->st_blocks = nfs_attr->used / 512;
327 buf->st_atim = nfs_attr->atime;
328 buf->st_mtim = nfs_attr->mtime;
329 buf->st_ctim = nfs_attr->ctime;
334 fhandler_base::fstat_by_handle (struct __stat64 *buf)
336 /* Don't use FileAllInformation info class. It returns a pathname rather
337 than a filename, so it needs a really big buffer for no good reason
338 since we don't need the name anyway. So we just call the three info
339 classes necessary to get all information required by stat(2). */
340 FILE_STANDARD_INFORMATION fsi;
341 FILE_INTERNAL_INFORMATION fii;
343 HANDLE h = get_stat_handle ();
347 /* If the file has been opened for other purposes than stat, we can't rely
348 on the information stored in pc.fnoi. So we overwrite them here. */
349 if (get_io_handle ())
351 PFILE_NETWORK_OPEN_INFORMATION pfnoi = pc.fnoi ();
352 status = NtQueryInformationFile (h, &io, pfnoi, sizeof *pfnoi,
353 FileNetworkOpenInformation);
354 if (!NT_SUCCESS (status))
356 debug_printf ("%p = NtQueryInformationFile(%S, "
357 "FileNetworkOpenInformation)",
358 status, pc.get_nt_native_path ());
362 if (!pc.hasgood_inode ())
363 fsi.NumberOfLinks = 1;
366 status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
367 FileStandardInformation);
368 if (!NT_SUCCESS (status))
370 debug_printf ("%p = NtQueryInformationFile(%S, "
371 "FileStandardInformation)",
372 status, pc.get_nt_native_path ());
377 status = NtQueryInformationFile (h, &io, &fii, sizeof fii,
378 FileInternalInformation);
379 if (!NT_SUCCESS (status))
381 debug_printf ("%p = NtQueryInformationFile(%S, "
382 "FileInternalInformation)",
383 status, pc.get_nt_native_path ());
386 ino = fii.FileId.QuadPart;
389 return fstat_helper (buf, fsi.NumberOfLinks);
393 fhandler_base::fstat_by_name (struct __stat64 *buf)
396 OBJECT_ATTRIBUTES attr;
398 UNICODE_STRING dirname;
399 UNICODE_STRING basename;
402 FILE_ID_BOTH_DIR_INFORMATION fdi;
403 WCHAR buf[NAME_MAX + 1];
406 if (!ino && pc.hasgood_inode ()
407 && wincap.has_fileid_dirinfo () && !pc.has_buggy_fileid_dirinfo ())
409 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
410 InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
412 status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
413 &attr, &io, FILE_SHARE_VALID_FLAGS,
414 FILE_SYNCHRONOUS_IO_NONALERT
415 | FILE_OPEN_FOR_BACKUP_INTENT
416 | FILE_DIRECTORY_FILE);
417 if (!NT_SUCCESS (status))
418 debug_printf ("%p = NtOpenFile(%S)", status,
419 pc.get_nt_native_path ());
422 status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
423 &fdi_buf.fdi, sizeof fdi_buf,
424 FileIdBothDirectoryInformation,
425 TRUE, &basename, TRUE);
427 if (!NT_SUCCESS (status))
428 debug_printf ("%p = NtQueryDirectoryFile(%S)", status,
429 pc.get_nt_native_path ());
431 ino = fdi_buf.fdi.FileId.QuadPart;
434 return fstat_helper (buf, 1);
438 fhandler_base::fstat_fs (struct __stat64 *buf)
442 int open_flags = O_RDONLY | O_BINARY;
444 if (get_stat_handle ())
446 if (!nohandle () && !is_fs_special ())
447 res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
449 res = fstat_by_name (buf);
452 /* First try to open with generic read access. This allows to read the file
453 in fstat_helper (when checking for executability) without having to
454 re-open it. Opening a file can take a lot of time on network drives
455 so we try to avoid that. */
456 oret = open_fs (open_flags, 0);
459 query_open (query_read_attributes);
460 oret = open_fs (open_flags, 0);
464 /* We now have a valid handle, regardless of the "nohandle" state.
465 Since fhandler_base::open only calls CloseHandle if !nohandle,
466 we have to set it to false before calling close and restore
467 the state afterwards. */
468 res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
469 bool no_handle = nohandle ();
472 nohandle (no_handle);
473 set_io_handle (NULL);
476 res = fstat_by_name (buf);
482 fhandler_base::fstat_helper (struct __stat64 *buf,
483 DWORD nNumberOfLinks)
486 FILE_COMPRESSION_INFORMATION fci;
487 HANDLE h = get_stat_handle ();
488 PFILE_NETWORK_OPEN_INFORMATION pfnoi = pc.fnoi ();
489 ULONG attributes = pc.file_attributes ();
491 to_timestruc_t ((PFILETIME) &pfnoi->LastAccessTime, &buf->st_atim);
492 to_timestruc_t ((PFILETIME) &pfnoi->LastWriteTime, &buf->st_mtim);
493 /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
494 (FAT for instance). If so, it's faked using LastWriteTime. */
495 to_timestruc_t (pfnoi->ChangeTime.QuadPart ? (PFILETIME) &pfnoi->ChangeTime
496 : (PFILETIME) &pfnoi->LastWriteTime,
498 to_timestruc_t ((PFILETIME) &pfnoi->CreationTime, &buf->st_birthtim);
499 buf->st_rdev = buf->st_dev = get_dev ();
500 buf->st_size = (_off64_t) pfnoi->EndOfFile.QuadPart;
501 /* The number of links to a directory includes the number of subdirectories
502 in the directory, since all those subdirectories point to it. However,
503 this is painfully slow, so we do without it. */
505 buf->st_nlink = pc.ndisk_links (nNumberOfLinks);
507 buf->st_nlink = nNumberOfLinks;
510 /* Enforce namehash as inode number on untrusted file systems. */
511 if (ino && pc.isgood_inode (ino))
512 buf->st_ino = (__ino64_t) ino;
514 buf->st_ino = get_ino ();
516 buf->st_blksize = PREFERRED_IO_BLKSIZE;
518 if (pfnoi->AllocationSize.QuadPart >= 0LL)
519 /* A successful NtQueryInformationFile returns the allocation size
520 correctly for compressed and sparse files as well. */
521 buf->st_blocks = (pfnoi->AllocationSize.QuadPart + S_BLKSIZE - 1)
523 else if (::has_attribute (attributes, FILE_ATTRIBUTE_COMPRESSED
524 | FILE_ATTRIBUTE_SPARSE_FILE)
525 && h && !is_fs_special ()
526 && !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci,
527 FileCompressionInformation))
528 /* Otherwise we request the actual amount of bytes allocated for
529 compressed and sparsed files. */
530 buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
533 /* Otherwise compute no. of blocks from file size. */
534 buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
537 /* Using a side effect: get_file_attributes checks for directory.
538 This is used, to set S_ISVTX, if needed. */
540 buf->st_mode = S_IFDIR;
541 else if (pc.issymlink ())
543 buf->st_size = pc.get_symlink_length ();
544 /* symlinks are everything for everyone! */
545 buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
546 get_file_attribute (h, pc, NULL,
547 &buf->st_uid, &buf->st_gid);
550 else if (pc.issocket ())
551 buf->st_mode = S_IFSOCK;
553 if (!get_file_attribute (is_fs_special () && !pc.issocket () ? NULL : h, pc,
554 &buf->st_mode, &buf->st_uid, &buf->st_gid))
556 /* If read-only attribute is set, modify ntsec return value */
557 if (::has_attribute (attributes, FILE_ATTRIBUTE_READONLY)
558 && !pc.isdir () && !pc.issymlink ())
559 buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
561 if (buf->st_mode & S_IFMT)
563 else if (!is_fs_special ())
564 buf->st_mode |= S_IFREG;
567 buf->st_dev = buf->st_rdev = dev ();
568 buf->st_mode = dev ().mode;
574 buf->st_mode |= STD_RBITS;
576 if (!::has_attribute (attributes, FILE_ATTRIBUTE_READONLY))
577 buf->st_mode |= STD_WBITS;
578 /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
581 buf->st_mode |= S_IFDIR | STD_WBITS | STD_XBITS;
582 else if (buf->st_mode & S_IFMT)
584 else if (is_fs_special ())
586 buf->st_dev = buf->st_rdev = dev ();
587 buf->st_mode = dev ().mode;
592 buf->st_mode |= S_IFREG;
593 /* Check suffix for executable file. */
594 if (pc.exec_state () == dont_know_if_executable)
596 PUNICODE_STRING path = pc.get_nt_native_path ();
598 if (RtlEqualUnicodePathSuffix (path, &ro_u_exe, TRUE)
599 || RtlEqualUnicodePathSuffix (path, &ro_u_lnk, TRUE)
600 || RtlEqualUnicodePathSuffix (path, &ro_u_com, TRUE))
603 /* No known suffix, check file header. This catches binaries and
605 if (pc.exec_state () == dont_know_if_executable)
607 LARGE_INTEGER off = { QuadPart:0LL };
613 if (h == get_handle ())
615 /* We have been opened via fstat. We have to re-open the
616 file. Either the file is not opened for reading, or the
617 read will change the file position. */
618 OBJECT_ATTRIBUTES attr;
619 pc.init_reopen_attr (&attr, h);
620 status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA,
621 &attr, &io, FILE_SHARE_VALID_FLAGS,
622 FILE_OPEN_FOR_BACKUP_INTENT
623 | FILE_OPEN_REPARSE_POINT);
624 if (!NT_SUCCESS (status))
625 debug_printf ("%p = NtOpenFile(%S)", status,
626 pc.get_nt_native_path ());
630 if (NT_SUCCESS (status))
632 status = NtReadFile (h, NULL, NULL, NULL,
633 &io, magic, 3, &off, NULL);
634 status = wait_pending (status, h, io);
635 if (!NT_SUCCESS (status))
636 debug_printf ("%p = NtReadFile(%S)", status,
637 pc.get_nt_native_path ());
638 else if (has_exec_chars (magic, io.Information))
640 /* Heureka, it's an executable */
642 buf->st_mode |= STD_XBITS;
649 if (pc.exec_state () == is_executable)
650 buf->st_mode |= STD_XBITS;
652 /* This fakes the permissions of all files to match the current umask. */
653 buf->st_mode &= ~(cygheap->umask);
654 /* If the FS supports ACLs, we're here because we couldn't even open
655 the file for READ_CONTROL access. Chances are high that the file's
656 security descriptor has no ACE for "Everyone", so we should not fake
657 any access for "others". */
659 buf->st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH);
663 syscall_printf ("0 = fstat (%S, %p) st_atime=%x st_size=%D, st_mode=%p, "
664 "st_ino=%D, sizeof=%d",
665 pc.get_nt_native_path (), buf, buf->st_atime, buf->st_size,
666 buf->st_mode, buf->st_ino, sizeof (*buf));
671 fhandler_disk_file::fstat (struct __stat64 *buf)
673 return fstat_fs (buf);
677 fhandler_disk_file::fstatvfs (struct statvfs *sfs)
679 int ret = -1, opened = 0;
682 FILE_FS_FULL_SIZE_INFORMATION full_fsi;
683 FILE_FS_SIZE_INFORMATION fsi;
684 /* We must not use the stat handle here, even if it exists. The handle
685 has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
686 mount point, it points to the FS of the mount point, rather than to the
688 HANDLE fh = get_handle ();
692 OBJECT_ATTRIBUTES attr;
693 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
694 pc.get_object_attr (attr, sec_none_nih),
695 &io, FILE_SHARE_VALID_FLAGS,
696 FILE_OPEN_FOR_BACKUP_INTENT));
699 /* Can't open file. Try again with parent dir. */
700 UNICODE_STRING dirname;
701 RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
702 attr.ObjectName = &dirname;
703 opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
704 FILE_SHARE_VALID_FLAGS,
705 FILE_OPEN_FOR_BACKUP_INTENT));
711 sfs->f_files = ULONG_MAX;
712 sfs->f_ffree = ULONG_MAX;
713 sfs->f_favail = ULONG_MAX;
714 sfs->f_fsid = pc.fs_serial_number ();
715 sfs->f_flag = pc.fs_flags ();
716 sfs->f_namemax = pc.fs_name_len ();
717 /* Get allocation related information. Try to get "full" information
718 first, which is only available since W2K. If that fails, try to
719 retrieve normal allocation information. */
720 status = NtQueryVolumeInformationFile (fh, &io, &full_fsi, sizeof full_fsi,
721 FileFsFullSizeInformation);
722 if (NT_SUCCESS (status))
724 sfs->f_bsize = full_fsi.BytesPerSector * full_fsi.SectorsPerAllocationUnit;
725 sfs->f_frsize = sfs->f_bsize;
726 sfs->f_blocks = full_fsi.TotalAllocationUnits.LowPart;
727 sfs->f_bfree = full_fsi.ActualAvailableAllocationUnits.LowPart;
728 sfs->f_bavail = full_fsi.CallerAvailableAllocationUnits.LowPart;
729 if (sfs->f_bfree > sfs->f_bavail)
731 /* Quotas active. We can't trust TotalAllocationUnits. */
732 NTFS_VOLUME_DATA_BUFFER nvdb;
734 status = NtFsControlFile (fh, NULL, NULL, NULL, &io,
735 FSCTL_GET_NTFS_VOLUME_DATA,
736 NULL, 0, &nvdb, sizeof nvdb);
737 if (!NT_SUCCESS (status))
738 debug_printf ("%p = NtFsControlFile(%S, FSCTL_GET_NTFS_VOLUME_DATA)",
739 status, pc.get_nt_native_path ());
741 sfs->f_blocks = nvdb.TotalClusters.QuadPart;
747 status = NtQueryVolumeInformationFile (fh, &io, &fsi, sizeof fsi,
748 FileFsSizeInformation);
749 if (!NT_SUCCESS (status))
751 __seterrno_from_nt_status (status);
754 sfs->f_bsize = fsi.BytesPerSector * fsi.SectorsPerAllocationUnit;
755 sfs->f_frsize = sfs->f_bsize;
756 sfs->f_blocks = fsi.TotalAllocationUnits.LowPart;
757 sfs->f_bfree = fsi.AvailableAllocationUnits.LowPart;
758 sfs->f_bavail = sfs->f_bfree;
764 syscall_printf ("%d = fstatvfs (%s, %p)", ret, get_name (), sfs);
769 fhandler_disk_file::fchmod (mode_t mode)
771 extern int chmod_device (path_conv& pc, mode_t mode);
777 if (pc.is_fs_special ())
778 return chmod_device (pc, mode);
782 query_open (query_write_dac);
783 if (!(oret = open (O_BINARY, 0)))
785 /* Need WRITE_DAC|WRITE_OWNER to write ACLs. */
788 /* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
789 query_open (query_write_attributes);
790 if (!(oret = open (O_BINARY, 0)))
797 /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
798 Only type and mode have to be set. Apparently type isn't checked
799 for consistency, so it's sufficent to set it to NF3REG all the time. */
801 FILE_FULL_EA_INFORMATION ffei;
802 char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
804 ffei_buf.ffei.NextEntryOffset = 0;
805 ffei_buf.ffei.Flags = 0;
806 ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
807 ffei_buf.ffei.EaValueLength = sizeof (fattr3);
808 strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
809 fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
810 + ffei_buf.ffei.EaNameLength + 1);
811 memset (nfs_attr, 0, sizeof (fattr3));
812 nfs_attr->type = NF3REG;
813 nfs_attr->mode = mode;
814 status = NtSetEaFile (get_handle (), &io,
815 &ffei_buf.ffei, sizeof ffei_buf);
816 if (!NT_SUCCESS (status))
817 __seterrno_from_nt_status (status);
827 if (!set_file_attribute (get_handle (), pc,
828 ILLEGAL_UID, ILLEGAL_GID, mode))
832 /* If the mode has any write bits set, the DOS R/O flag is in the way. */
833 if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
834 pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
835 else if (!pc.has_acls ()) /* Never set DOS R/O if security is used. */
836 pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
838 pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
840 status = NtSetAttributesFile (get_handle (), pc.file_attributes ());
841 /* MVFS needs a good amount of kicking to be convinced that it has to write
842 back metadata changes and to invalidate the cached metadata information
843 stored for the given handle. This method to open a second handle to
844 the file and write the same metadata information twice has been found
845 experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
846 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !oret)
848 OBJECT_ATTRIBUTES attr;
851 pc.init_reopen_attr (&attr, get_handle ());
852 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
853 FILE_SHARE_VALID_FLAGS,
854 FILE_OPEN_FOR_BACKUP_INTENT)))
856 NtSetAttributesFile (fh, pc.file_attributes ());
860 /* Correct NTFS security attributes have higher priority */
863 if (!NT_SUCCESS (status))
864 __seterrno_from_nt_status (status);
877 fhandler_disk_file::fchown (__uid32_t uid, __gid32_t gid)
883 /* fake - if not supported, pretend we're like win95
884 where it just works */
885 /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
891 query_open (query_write_control);
892 if (!(oret = fhandler_disk_file::open (O_BINARY, 0)))
900 int res = get_file_attribute (get_handle (), pc, &attrib, &old_uid, NULL);
903 /* Typical Windows default ACLs can contain permissions for one
904 group, while being owned by another user/group. The permission
905 bits returned above are pretty much useless then. Creating a
906 new ACL with these useless permissions results in a potentially
907 broken symlink. So what we do here is to set the underlying
908 permissions of symlinks to a sensible value which allows the
909 world to read the symlink and only the new owner to change it. */
911 attrib = S_IFLNK | STD_RBITS | STD_WBITS;
912 res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
913 /* If you're running a Samba server which has no winbidd running, the
914 uid<->SID mapping is disfunctional. Even trying to chown to your
915 own account fails since the account used on the server is the UNIX
916 account which gets used for the standard user mapping. This is a
917 default mechanism which doesn't know your real Windows SID.
918 There are two possible error codes in different Samba releases for
919 this situation, one of them is unfortunately the not very significant
920 STATUS_ACCESS_DENIED. Instead of relying on the error codes, we're
921 using the below very simple heuristic. If set_file_attribute failed,
922 and the original user account was either already unknown, or one of
923 the standard UNIX accounts, we're faking success. */
924 if (res == -1 && pc.fs_is_samba ())
928 if (old_uid == ILLEGAL_UID
929 || (sid.getfrompw (internal_getpwuid (old_uid))
930 && EqualPrefixSid (sid, well_known_samba_unix_user_fake_sid)))
932 debug_printf ("Faking chown worked on standalone Samba");
944 fhandler_disk_file::facl (int cmd, int nentries, __aclent32_t *aclbufp)
957 /* Open for writing required to be able to set ctime
958 (even though setting the ACL is just pretended). */
960 oret = open (O_WRONLY | O_BINARY, 0);
966 else if (nentries < MIN_ACL_ENTRIES)
972 aclbufp[0].a_type = USER_OBJ;
973 aclbufp[0].a_id = st.st_uid;
974 aclbufp[0].a_perm = (st.st_mode & S_IRWXU) >> 6;
975 aclbufp[1].a_type = GROUP_OBJ;
976 aclbufp[1].a_id = st.st_gid;
977 aclbufp[1].a_perm = (st.st_mode & S_IRWXG) >> 3;
978 aclbufp[2].a_type = OTHER_OBJ;
979 aclbufp[2].a_id = ILLEGAL_GID;
980 aclbufp[2].a_perm = st.st_mode & S_IRWXO;
981 aclbufp[3].a_type = CLASS_OBJ;
982 aclbufp[3].a_id = ILLEGAL_GID;
983 aclbufp[3].a_perm = S_IRWXU | S_IRWXG | S_IRWXO;
984 res = MIN_ACL_ENTRIES;
989 res = MIN_ACL_ENTRIES;
998 if ((cmd == SETACL && !get_handle ())
999 || (cmd != SETACL && !get_stat_handle ()))
1001 query_open (cmd == SETACL ? query_write_control : query_read_control);
1002 if (!(oret = open (O_BINARY, 0)))
1004 if (cmd == GETACL || cmd == GETACLCNT)
1005 goto cant_access_acl;
1012 if (!aclsort32 (nentries, 0, aclbufp))
1015 res = setacl (get_handle (), pc, nentries, aclbufp, rw);
1019 FILE_BASIC_INFORMATION fbi;
1020 fbi.CreationTime.QuadPart
1021 = fbi.LastAccessTime.QuadPart
1022 = fbi.LastWriteTime.QuadPart
1023 = fbi.ChangeTime.QuadPart = 0LL;
1024 fbi.FileAttributes = (pc.file_attributes ()
1025 & ~FILE_ATTRIBUTE_READONLY)
1026 ?: FILE_ATTRIBUTE_NORMAL;
1027 NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1028 FileBasicInformation);
1036 res = getacl (get_stat_handle (), pc, nentries, aclbufp);
1037 /* For this ENOSYS case, see security.cc:get_file_attribute(). */
1038 if (res == -1 && get_errno () == ENOSYS)
1039 goto cant_access_acl;
1042 res = getacl (get_stat_handle (), pc, 0, NULL);
1044 if (res == -1 && get_errno () == ENOSYS)
1045 goto cant_access_acl;
1060 fhandler_disk_file::fgetxattr (const char *name, void *value, size_t size)
1062 if (pc.is_fs_special ())
1064 set_errno (ENOTSUP);
1067 return read_ea (get_handle (), pc, name, (char *) value, size);
1071 fhandler_disk_file::fsetxattr (const char *name, const void *value, size_t size,
1074 if (pc.is_fs_special ())
1076 set_errno (ENOTSUP);
1079 return write_ea (get_handle (), pc, name, (const char *) value, size, flags);
1083 fhandler_disk_file::fadvise (_off64_t offset, _off64_t length, int advice)
1085 if (advice < POSIX_FADV_NORMAL || advice > POSIX_FADV_NOREUSE)
1091 /* Windows only supports advice flags for the whole file. We're using
1092 a simplified test here so that we don't have to ask for the actual
1093 file size. Length == 0 means all bytes starting at offset anyway.
1094 So we only actually follow the advice, if it's given for offset == 0. */
1098 /* We only support normal and sequential mode for now. Everything which
1099 is not POSIX_FADV_SEQUENTIAL is treated like POSIX_FADV_NORMAL. */
1100 if (advice != POSIX_FADV_SEQUENTIAL)
1101 advice = POSIX_FADV_NORMAL;
1104 FILE_MODE_INFORMATION fmi;
1105 NTSTATUS status = NtQueryInformationFile (get_handle (), &io,
1107 FileModeInformation);
1108 if (!NT_SUCCESS (status))
1109 __seterrno_from_nt_status (status);
1112 fmi.Mode &= ~FILE_SEQUENTIAL_ONLY;
1113 if (advice == POSIX_FADV_SEQUENTIAL)
1114 fmi.Mode |= FILE_SEQUENTIAL_ONLY;
1115 status = NtSetInformationFile (get_handle (), &io, &fmi, sizeof fmi,
1116 FileModeInformation);
1117 if (NT_SUCCESS (status))
1119 __seterrno_from_nt_status (status);
1126 fhandler_disk_file::ftruncate (_off64_t length, bool allow_truncate)
1130 if (length < 0 || !get_handle ())
1132 else if (pc.isdir ())
1134 else if (!(get_access () & GENERIC_WRITE))
1140 FILE_STANDARD_INFORMATION fsi;
1141 FILE_END_OF_FILE_INFORMATION feofi;
1143 status = NtQueryInformationFile (get_handle (), &io, &fsi, sizeof fsi,
1144 FileStandardInformation);
1145 if (!NT_SUCCESS (status))
1147 __seterrno_from_nt_status (status);
1151 /* If called through posix_fallocate, silently succeed if length
1152 is less than the file's actual length. */
1153 if (!allow_truncate && length < fsi.EndOfFile.QuadPart)
1156 feofi.EndOfFile.QuadPart = length;
1157 /* Create sparse files only when called through ftruncate, not when
1158 called through posix_fallocate. */
1160 && (pc.fs_flags () & FILE_SUPPORTS_SPARSE_FILES)
1161 && length >= fsi.EndOfFile.QuadPart + (128 * 1024))
1163 status = NtFsControlFile (get_handle (), NULL, NULL, NULL, &io,
1164 FSCTL_SET_SPARSE, NULL, 0, NULL, 0);
1165 syscall_printf ("%p = NtFsControlFile(%S, FSCTL_SET_SPARSE)",
1166 status, pc.get_nt_native_path ());
1168 status = NtSetInformationFile (get_handle (), &io,
1169 &feofi, sizeof feofi,
1170 FileEndOfFileInformation);
1171 if (!NT_SUCCESS (status))
1172 __seterrno_from_nt_status (status);
1180 fhandler_disk_file::link (const char *newpath)
1182 size_t nlen = strlen (newpath);
1183 path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
1186 set_errno (newpc.error);
1190 if (newpc.exists ())
1192 syscall_printf ("file '%S' exists?", newpc.get_nt_native_path ());
1197 if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
1203 char new_buf[nlen + 5];
1206 /* If the original file is a lnk special file (except for sockets),
1207 and if the original file has a .lnk suffix, add one to the hardlink
1209 if (pc.is_lnk_special () && !pc.issocket ()
1210 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1213 /* Shortcut hack. */
1214 stpcpy (stpcpy (new_buf, newpath), ".lnk");
1216 newpc.check (newpath, PC_SYM_NOFOLLOW);
1218 else if (!pc.isdir ()
1220 && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
1222 && !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
1225 /* Executable hack. */
1226 stpcpy (stpcpy (new_buf, newpath), ".exe");
1228 newpc.check (newpath, PC_SYM_NOFOLLOW);
1232 /* We only need READ_CONTROL access so the handle returned in pc is
1233 sufficient. And if the file couldn't be opened with READ_CONTROL
1234 access in path_conv, we won't be able to do it here anyway. */
1235 HANDLE fh = get_stat_handle ();
1241 PUNICODE_STRING tgt = newpc.get_nt_native_path ();
1242 ULONG size = sizeof (FILE_LINK_INFORMATION) + tgt->Length;
1243 PFILE_LINK_INFORMATION pfli = (PFILE_LINK_INFORMATION) alloca (size);
1244 pfli->ReplaceIfExists = FALSE;
1245 pfli->RootDirectory = NULL;
1246 memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
1250 status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
1251 if (!NT_SUCCESS (status))
1253 if (status == STATUS_INVALID_DEVICE_REQUEST)
1255 /* FS doesn't support hard links. Linux returns EPERM. */
1261 __seterrno_from_nt_status (status);
1269 fhandler_disk_file::utimens (const struct timespec *tvp)
1271 return utimens_fs (tvp);
1275 fhandler_base::utimens_fs (const struct timespec *tvp)
1277 struct timespec timeofday;
1278 struct timespec tmp[2];
1279 bool closeit = false;
1283 query_open (query_write_attributes);
1284 if (!open_fs (O_BINARY, 0))
1286 /* It's documented in MSDN that FILE_WRITE_ATTRIBUTES is sufficient
1287 to change the timestamps. Unfortunately it's not sufficient for a
1288 remote HPFS which requires GENERIC_WRITE, so we just retry to open
1289 for writing, though this fails for R/O files of course. */
1290 query_open (no_query);
1291 if (!open_fs (O_WRONLY | O_BINARY, 0))
1293 syscall_printf ("Opening file failed");
1300 clock_gettime (CLOCK_REALTIME, &timeofday);
1302 tmp[1] = tmp[0] = timeofday;
1305 if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
1306 || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
1313 tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
1314 tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
1316 debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
1319 FILE_BASIC_INFORMATION fbi;
1321 fbi.CreationTime.QuadPart = 0LL;
1322 /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
1323 timespec_to_filetime (&tmp[0], (LPFILETIME) &fbi.LastAccessTime);
1324 timespec_to_filetime (&tmp[1], (LPFILETIME) &fbi.LastWriteTime);
1325 fbi.ChangeTime.QuadPart = 0LL;
1326 fbi.FileAttributes = 0;
1327 NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
1328 FileBasicInformation);
1329 /* For this special case for MVFS see the comment in
1330 fhandler_disk_file::fchmod. */
1331 if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
1333 OBJECT_ATTRIBUTES attr;
1336 pc.init_reopen_attr (&attr, get_handle ());
1337 if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
1338 FILE_SHARE_VALID_FLAGS,
1339 FILE_OPEN_FOR_BACKUP_INTENT)))
1341 NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
1342 FileBasicInformation);
1348 /* Opening a directory on a 9x share from a NT machine works(!), but
1349 then NtSetInformationFile fails with STATUS_NOT_SUPPORTED. Oh well... */
1350 if (!NT_SUCCESS (status) && status != STATUS_NOT_SUPPORTED)
1352 __seterrno_from_nt_status (status);
1358 fhandler_disk_file::fhandler_disk_file () :
1363 fhandler_disk_file::fhandler_disk_file (path_conv &pc) :
1370 fhandler_disk_file::open (int flags, mode_t mode)
1372 return open_fs (flags, mode);
1376 fhandler_base::open_fs (int flags, mode_t mode)
1378 /* Unfortunately NT allows to open directories for writing, but that's
1379 disallowed according to SUSv3. */
1380 if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
1386 int res = fhandler_base::open (flags | O_DIROPEN, mode);
1390 /* This is for file systems known for having a buggy CreateFile call
1391 which might return a valid HANDLE without having actually opened
1393 The only known file system to date is the SUN NFS Solstice Client 3.1
1394 which returns a valid handle when trying to open a file in a nonexistent
1396 if (pc.has_buggy_open () && !pc.exists ())
1398 debug_printf ("Buggy open detected.");
1404 ino = pc.get_ino_by_handle (get_handle ());
1405 /* A unique ID is necessary to recognize fhandler entries which are
1406 duplicated by dup(2) or fork(2). */
1407 AllocateLocallyUniqueId ((PLUID) &unique_id);
1410 syscall_printf ("%d = fhandler_disk_file::open (%S, %p)", res,
1411 pc.get_nt_native_path (), flags);
1416 fhandler_disk_file::pread (void *buf, size_t count, _off64_t offset)
1419 _off64_t curpos = lseek (0, SEEK_CUR);
1420 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1424 size_t tmp_count = count;
1425 read (buf, tmp_count);
1426 if (lseek (curpos, SEEK_SET) >= 0)
1427 res = (ssize_t) tmp_count;
1431 debug_printf ("%d = pread (%p, %d, %d)\n", res, buf, count, offset);
1436 fhandler_disk_file::pwrite (void *buf, size_t count, _off64_t offset)
1439 _off64_t curpos = lseek (0, SEEK_CUR);
1440 if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
1444 res = (ssize_t) write (buf, count);
1445 if (lseek (curpos, SEEK_SET) < 0)
1448 debug_printf ("%d = pwrite (%p, %d, %d)\n", res, buf, count, offset);
1453 fhandler_disk_file::mkdir (mode_t mode)
1456 SECURITY_ATTRIBUTES sa = sec_none_nih;
1459 OBJECT_ATTRIBUTES attr;
1461 PFILE_FULL_EA_INFORMATION p = NULL;
1464 if (pc.fs_is_nfs ())
1466 /* When creating a dir on an NFS share, we have to set the
1467 file mode by writing a NFS fattr3 structure with the
1468 correct mode bits set. */
1469 plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
1471 p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
1472 p->NextEntryOffset = 0;
1474 p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
1475 p->EaValueLength = sizeof (fattr3);
1476 strcpy (p->EaName, NFS_V3_ATTR);
1477 fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
1478 memset (nfs_attr, 0, sizeof (fattr3));
1479 nfs_attr->type = NF3DIR;
1480 nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
1482 status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
1483 pc.get_object_attr (attr, sa), &io, NULL,
1484 FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
1486 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
1487 | FILE_OPEN_FOR_BACKUP_INTENT,
1489 if (NT_SUCCESS (status))
1492 set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
1493 S_JUSTCREATED | S_IFDIR
1494 | ((mode & 07777) & ~cygheap->umask));
1499 __seterrno_from_nt_status (status);
1505 fhandler_disk_file::rmdir ()
1507 extern NTSTATUS unlink_nt (path_conv &pc);
1511 set_errno (ENOTDIR);
1520 NTSTATUS status = unlink_nt (pc);
1522 /* Check for existence of remote dirs after trying to delete them.
1524 - Sometimes SMB indicates failure when it really succeeds.
1525 - Removing a directory on a Samba drive using an old Samba version
1526 sometimes doesn't return an error, if the directory can't be removed
1527 because it's not empty. */
1530 OBJECT_ATTRIBUTES attr;
1531 FILE_BASIC_INFORMATION fbi;
1534 q_status = NtQueryAttributesFile (pc.get_object_attr (attr, sec_none_nih), &fbi);
1535 if (!NT_SUCCESS (status) && q_status == STATUS_OBJECT_NAME_NOT_FOUND)
1536 status = STATUS_SUCCESS;
1537 else if (NT_SUCCESS (status) && NT_SUCCESS (q_status))
1538 status = STATUS_DIRECTORY_NOT_EMPTY;
1540 if (!NT_SUCCESS (status))
1542 __seterrno_from_nt_status (status);
1548 /* This is the minimal number of entries which fit into the readdir cache.
1549 The number of bytes allocated by the cache is determined by this number,
1550 To tune caching, just tweak this number. To get a feeling for the size,
1551 the size of the readdir cache is DIR_NUM_ENTRIES * 624 + 4 bytes. */
1553 #define DIR_NUM_ENTRIES 100 /* Cache size 62404 bytes */
1555 #define DIR_BUF_SIZE (DIR_NUM_ENTRIES \
1556 * (sizeof (FILE_ID_BOTH_DIR_INFORMATION) \
1557 + (NAME_MAX + 1) * sizeof (WCHAR)))
1561 char __cache[DIR_BUF_SIZE]; /* W2K needs this buffer 8 byte aligned. */
1565 #define d_cachepos(d) (((__DIR_cache *) (d)->__d_dirname)->__pos)
1566 #define d_cache(d) (((__DIR_cache *) (d)->__d_dirname)->__cache)
1568 #define d_mounts(d) ((__DIR_mounts *) (d)->__d_internal)
1571 fhandler_disk_file::opendir (int fd)
1577 set_errno (ENOTDIR);
1578 else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
1580 else if ((dir->__d_dirname = (char *) malloc ( sizeof (struct __DIR_cache)))
1586 else if ((dir->__d_dirent =
1587 (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
1595 if (cfd < 0 && fd < 0)
1598 dir->__d_dirent->__d_version = __DIRENT_VERSION;
1599 dir->__d_cookie = __DIRENT_COOKIE;
1600 dir->__handle = INVALID_HANDLE_VALUE;
1601 dir->__d_position = 0;
1602 dir->__flags = (get_name ()[0] == '/' && get_name ()[1] == '\0')
1603 ? dirent_isroot : 0;
1604 dir->__d_internal = (unsigned) new __DIR_mounts (get_name ());
1605 d_cachepos (dir) = 0;
1607 if (!pc.iscygdrive ())
1611 /* opendir() case. Initialize with given directory name and
1612 NULL directory handle. */
1613 OBJECT_ATTRIBUTES attr;
1616 /* Tools like ls(1) call dirfd() to fetch the directory
1617 descriptor for calls to facl or fstat. The tight access mask
1618 used so far is not sufficient to reuse the handle for these
1619 calls, instead the facl/fstat calls find the handle to be
1620 unusable and have to re-open the file for reading attributes
1621 and control data. So, what we do here is to try to open the
1622 directory with more relaxed access mask which enables to use
1623 the handle for the aforementioned purpose. This should work
1624 in almost all cases. Only if it doesn't work due to
1625 permission problems, we drop the additional access bits and
1627 ACCESS_MASK fstat_mask = READ_CONTROL | FILE_READ_ATTRIBUTES;
1631 status = NtOpenFile (&get_handle (),
1632 SYNCHRONIZE | FILE_LIST_DIRECTORY
1634 pc.get_object_attr (attr, sec_none_nih),
1635 &io, FILE_SHARE_VALID_FLAGS,
1636 FILE_SYNCHRONOUS_IO_NONALERT
1637 | FILE_OPEN_FOR_BACKUP_INTENT
1638 | FILE_DIRECTORY_FILE);
1639 if (!NT_SUCCESS (status))
1641 if (status == STATUS_ACCESS_DENIED && fstat_mask)
1645 __seterrno_from_nt_status (status);
1650 while (!NT_SUCCESS (status));
1653 /* FileIdBothDirectoryInformation is apparently unsupported on
1654 XP when accessing directories on UDF. When trying to use it
1655 so, NtQueryDirectoryFile returns with STATUS_ACCESS_VIOLATION.
1656 It's not clear if the call isn't also unsupported on other
1657 OS/FS combinations (say, Win2K/CDFS or so). Instead of
1658 testing in readdir for yet another error code, let's use
1659 FileIdBothDirectoryInformation only on filesystems supporting
1660 persistent ACLs, FileBothDirectoryInformation otherwise.
1662 NFS clients hide dangling symlinks from directory queries,
1663 unless you use the FileNamesInformation info class.
1664 On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
1665 works fine, but only if the NFS share is mounted to a drive
1666 letter. TODO: We don't test that here for now, but it might
1667 be worth to test if there's a speed gain in using
1668 FileIdBothDirectoryInformation, because it doesn't require to
1669 open the file to read the inode number. */
1670 if (pc.hasgood_inode ())
1672 dir->__flags |= dirent_set_d_ino;
1673 if (pc.fs_is_nfs ())
1674 dir->__flags |= dirent_nfs_d_ino;
1675 else if (wincap.has_fileid_dirinfo ()
1676 && !pc.has_buggy_fileid_dirinfo ())
1677 dir->__flags |= dirent_get_d_ino;
1684 /* Filling cfd with `this' (aka storing this in the file
1685 descriptor table should only happen after it's clear that
1686 opendir doesn't fail, otherwise we end up cfree'ing the
1687 fhandler twice, once in opendir() in dir.cc, the second
1688 time on exit. Nasty, nasty... */
1691 if (pc.iscygdrive ())
1692 cfd->nohandle (true);
1694 set_close_on_exec (true);
1699 syscall_printf ("%p = opendir (%s)", res, get_name ());
1703 delete d_mounts (dir);
1705 free (dir->__d_dirent);
1707 free (dir->__d_dirname);
1714 readdir_get_ino (const char *path, bool dot_dot)
1719 OBJECT_ATTRIBUTES attr;
1725 fname = (char *) alloca (strlen (path) + 4);
1726 char *c = stpcpy (fname, path);
1732 path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN | PC_KEEP_HANDLE);
1733 if (pc.isspecial ())
1735 if (!stat_worker (pc, &st))
1738 else if (!pc.hasgood_inode ())
1739 ino = hash_path_name (0, pc.get_nt_native_path ());
1740 else if ((hdl = pc.handle ()) != NULL
1741 || NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
1742 pc.get_object_attr (attr, sec_none_nih),
1743 &io, FILE_SHARE_VALID_FLAGS,
1744 FILE_OPEN_FOR_BACKUP_INTENT
1745 | (pc.is_rep_symlink ()
1746 ? FILE_OPEN_REPARSE_POINT : 0)))
1749 ino = pc.get_ino_by_handle (hdl);
1751 ino = hash_path_name (0, pc.get_nt_native_path ());
1757 fhandler_disk_file::readdir_helper (DIR *dir, dirent *de, DWORD w32_err,
1758 DWORD attr, PUNICODE_STRING fname)
1763 if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
1768 return geterrno_from_win_error (w32_err);
1772 dir->__flags &= ~dirent_set_d_ino;
1775 /* Set d_type if type can be determined from file attributes. For .lnk
1776 symlinks, d_type will be reset below. Reparse points can be NTFS
1777 symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
1779 !(attr & (~FILE_ATTRIBUTE_VALID_FLAGS | FILE_ATTRIBUTE_REPARSE_POINT)))
1781 if (attr & FILE_ATTRIBUTE_DIRECTORY)
1782 de->d_type = DT_DIR;
1783 /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
1784 else if (!(attr & FILE_ATTRIBUTE_SYSTEM))
1785 de->d_type = DT_REG;
1788 /* Check for directory reparse point. These are potential volume mount
1789 points which have another inode than the underlying directory. */
1790 if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1791 == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
1794 OBJECT_ATTRIBUTES attr;
1797 InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
1798 get_handle (), NULL);
1799 de->d_type = readdir_check_reparse_point (&attr);
1800 if (de->d_type == DT_DIR)
1802 /* Volume mountpoints are treated as directories. We have to fix
1803 the inode number, otherwise we have the inode number of the
1804 mount point, rather than the inode number of the toplevel
1805 directory of the mounted drive. */
1806 if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
1807 FILE_SHARE_VALID_FLAGS,
1808 FILE_OPEN_FOR_BACKUP_INTENT)))
1810 de->d_ino = pc.get_ino_by_handle (reph);
1816 /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
1817 .lnk suffix and set d_type accordingly. */
1818 if ((attr & (FILE_ATTRIBUTE_DIRECTORY
1819 | FILE_ATTRIBUTE_REPARSE_POINT
1820 | FILE_ATTRIBUTE_READONLY)) == FILE_ATTRIBUTE_READONLY
1821 && fname->Length > 4 * sizeof (WCHAR))
1823 UNICODE_STRING uname;
1825 RtlInitCountedUnicodeString (&uname,
1827 + fname->Length / sizeof (WCHAR) - 4,
1828 4 * sizeof (WCHAR));
1829 if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
1832 UNICODE_STRING fbuf;
1835 RtlCopyUnicodeString (&fbuf, pc.get_nt_native_path ());
1836 RtlAppendUnicodeToString (&fbuf, L"\\");
1837 RtlAppendUnicodeStringToString (&fbuf, fname);
1838 fbuf.Buffer += 4; /* Skip leading \??\ */
1839 fbuf.Length -= 4 * sizeof (WCHAR);
1840 if (fbuf.Buffer[1] != L':') /* UNC path */
1842 *(fbuf.Buffer += 2) = L'\\';
1843 fbuf.Length -= 2 * sizeof (WCHAR);
1845 path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
1846 if (fpath.issymlink ())
1848 fname->Length -= 4 * sizeof (WCHAR);
1849 de->d_type = DT_LNK;
1851 else if (fpath.isfifo ())
1853 fname->Length -= 4 * sizeof (WCHAR);
1854 de->d_type = DT_FIFO;
1856 else if (fpath.is_fs_special ())
1858 fname->Length -= 4 * sizeof (WCHAR);
1859 de->d_type = S_ISCHR (fpath.dev.mode) ? DT_CHR : DT_BLK;
1864 sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
1865 fname->Length / sizeof (WCHAR));
1867 /* Don't try to optimize relative to dir->__d_position. On several
1868 filesystems it's no safe bet that "." and ".." entries always
1870 if (de->d_name[0] == '.')
1872 if (de->d_name[1] == '\0')
1873 dir->__flags |= dirent_saw_dot;
1874 else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
1875 dir->__flags |= dirent_saw_dot_dot;
1881 fhandler_disk_file::readdir (DIR *dir, dirent *de)
1884 NTSTATUS status = STATUS_SUCCESS;
1885 PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
1887 ULONG FileNameLength;
1888 ULONG FileAttributes = 0;
1890 UNICODE_STRING fname;
1892 /* d_cachepos always refers to the next cache entry to use. If it's 0
1893 we must reload the cache. */
1894 if (d_cachepos (dir) == 0)
1896 if ((dir->__flags & dirent_get_d_ino))
1898 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1899 d_cache (dir), DIR_BUF_SIZE,
1900 FileIdBothDirectoryInformation,
1901 FALSE, NULL, dir->__d_position == 0);
1902 /* FileIdBothDirectoryInformation isn't supported for remote drives
1903 on NT4 and 2K systems, and it's also not supported on 2K at all,
1904 when accessing network drives on any remote OS. There are also
1905 hacked versions of Samba 3.0.x out there (Debian-based it seems),
1906 which return STATUS_NOT_SUPPORTED rather than handling this info
1907 class. We just fall back to using a standard directory query in
1908 this case and note this case using the dirent_get_d_ino flag. */
1909 if (!NT_SUCCESS (status) && status != STATUS_NO_MORE_FILES
1910 && (status == STATUS_INVALID_LEVEL
1911 || status == STATUS_NOT_SUPPORTED
1912 || status == STATUS_INVALID_PARAMETER
1913 || status == STATUS_INVALID_NETWORK_RESPONSE
1914 || status == STATUS_INVALID_INFO_CLASS))
1915 dir->__flags &= ~dirent_get_d_ino;
1916 /* Something weird happens on Samba up to version 3.0.21c, which is
1917 fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
1918 nicely, but only up to the 128th entry in the directory. After
1919 reaching this entry, the next call to NtQueryDirectoryFile
1920 (FileIdBothDirectoryInformation) returns STATUS_INVALID_LEVEL.
1921 Why should we care, we can just switch to
1922 FileBothDirectoryInformation, isn't it? Nope! The next call to
1923 NtQueryDirectoryFile(FileBothDirectoryInformation) actually
1924 returns STATUS_NO_MORE_FILES, regardless how many files are left
1925 unread in the directory. This does not happen when using
1926 FileBothDirectoryInformation right from the start, but since
1927 we can't decide whether the server we're talking with has this
1928 bug or not, we end up serving Samba shares always in the slow
1929 mode using FileBothDirectoryInformation. So, what we do here is
1930 to implement the solution suggested by Andrew Tridgell, we just
1931 reread all entries up to dir->d_position using
1932 FileBothDirectoryInformation.
1933 However, We do *not* mark this server as broken and fall back to
1934 using FileBothDirectoryInformation further on. This would slow
1935 down every access to such a server, even for directories under
1936 128 entries. Also, bigger dirs only suffer from one additional
1937 call per full directory scan, which shouldn't be too big a hit.
1938 This can easily be changed if necessary. */
1939 if (status == STATUS_INVALID_LEVEL && dir->__d_position)
1941 d_cachepos (dir) = 0;
1942 for (int cnt = 0; cnt < dir->__d_position; ++cnt)
1944 if (d_cachepos (dir) == 0)
1946 status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
1947 NULL, &io, d_cache (dir),
1949 FileBothDirectoryInformation,
1950 FALSE, NULL, cnt == 0);
1951 if (!NT_SUCCESS (status))
1954 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir)
1955 + d_cachepos (dir));
1956 if (buf->NextEntryOffset == 0)
1957 d_cachepos (dir) = 0;
1959 d_cachepos (dir) += buf->NextEntryOffset;
1964 if (!(dir->__flags & dirent_get_d_ino))
1965 status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
1966 d_cache (dir), DIR_BUF_SIZE,
1967 (dir->__flags & dirent_nfs_d_ino)
1968 ? FileNamesInformation
1969 : FileBothDirectoryInformation,
1970 FALSE, NULL, dir->__d_position == 0);
1975 if (status == STATUS_NO_MORE_FILES)
1977 else if (!NT_SUCCESS (status))
1978 debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
1979 status, RtlNtStatusToDosError (status));
1982 buf = (PFILE_ID_BOTH_DIR_INFORMATION) (d_cache (dir) + d_cachepos (dir));
1983 if (buf->NextEntryOffset == 0)
1984 d_cachepos (dir) = 0;
1986 d_cachepos (dir) += buf->NextEntryOffset;
1987 if ((dir->__flags & dirent_get_d_ino))
1989 FileName = buf->FileName;
1990 FileNameLength = buf->FileNameLength;
1991 FileAttributes = buf->FileAttributes;
1992 if ((dir->__flags & dirent_set_d_ino))
1993 de->d_ino = buf->FileId.QuadPart;
1995 else if ((dir->__flags & dirent_nfs_d_ino))
1997 FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
1998 FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
2002 FileName = ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileName;
2004 ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileNameLength;
2006 ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileAttributes;
2008 RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
2009 de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
2010 if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
2012 /* Don't try to optimize relative to dir->__d_position. On several
2013 filesystems it's no safe bet that "." and ".." entries always
2015 if (FileNameLength == sizeof (WCHAR) && FileName[0] == '.')
2016 de->d_ino = pc.get_ino_by_handle (get_handle ());
2017 else if (FileNameLength == 2 * sizeof (WCHAR)
2018 && FileName[0] == L'.' && FileName[1] == L'.')
2020 if (!(dir->__flags & dirent_isroot))
2021 de->d_ino = readdir_get_ino (get_name (), true);
2023 de->d_ino = pc.get_ino_by_handle (get_handle ());
2027 OBJECT_ATTRIBUTES attr;
2031 InitializeObjectAttributes (&attr, &fname,
2032 pc.objcaseinsensitive (),
2033 get_handle (), NULL);
2034 /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
2035 NtOpenFile here returns the inode number of the symlink target,
2036 rather than the inode number of the symlink itself.
2038 Worse, trying to open a symlink without setting the special
2039 "ActOnSymlink" EA triggers a bug in Windows 7 which results
2040 in a timeout of up to 20 seconds, followed by two exceptions
2043 Since both results are far from desirable, we open symlinks
2044 on NFS so that we get the right inode and a happy W7.
2045 And, since some filesystems choke on the EAs, we don't
2046 use them unconditionally. */
2047 f_status = (dir->__flags & dirent_nfs_d_ino)
2048 ? NtCreateFile (&hdl, READ_CONTROL, &attr, &io,
2049 NULL, 0, FILE_SHARE_VALID_FLAGS,
2050 FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT,
2051 &nfs_aol_ffei, sizeof nfs_aol_ffei)
2052 : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
2053 FILE_SHARE_VALID_FLAGS,
2054 FILE_OPEN_FOR_BACKUP_INTENT
2055 | FILE_OPEN_REPARSE_POINT);
2056 if (NT_SUCCESS (f_status))
2058 /* We call NtQueryInformationFile here, rather than
2059 pc.get_ino_by_handle(), otherwise we can't short-circuit
2060 dirent_set_d_ino correctly. */
2061 FILE_INTERNAL_INFORMATION fai;
2062 f_status = NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
2063 FileInternalInformation);
2065 if (NT_SUCCESS (f_status))
2067 if (pc.isgood_inode (fai.FileId.QuadPart))
2068 de->d_ino = fai.FileId.QuadPart;
2070 /* Untrusted file system. Don't try to fetch inode
2072 dir->__flags &= ~dirent_set_d_ino;
2079 if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
2080 FileAttributes, &fname)))
2081 dir->__d_position++;
2082 else if (!(dir->__flags & dirent_saw_dot))
2084 strcpy (de->d_name , ".");
2085 de->d_ino = pc.get_ino_by_handle (get_handle ());
2086 de->d_type = DT_DIR;
2087 dir->__d_position++;
2088 dir->__flags |= dirent_saw_dot;
2091 else if (!(dir->__flags & dirent_saw_dot_dot))
2093 strcpy (de->d_name , "..");
2094 if (!(dir->__flags & dirent_isroot))
2095 de->d_ino = readdir_get_ino (get_name (), true);
2097 de->d_ino = pc.get_ino_by_handle (get_handle ());
2098 de->d_type = DT_DIR;
2099 dir->__d_position++;
2100 dir->__flags |= dirent_saw_dot_dot;
2104 syscall_printf ("%d = readdir (%p, %p) (L\"%lS\" > \"%ls\") (attr %p > type %d)",
2105 res, dir, &de, res ? NULL : &fname, res ? "***" : de->d_name,
2106 FileAttributes, de->d_type);
2111 fhandler_disk_file::telldir (DIR *dir)
2113 return dir->__d_position;
2117 fhandler_disk_file::seekdir (DIR *dir, long loc)
2120 while (loc > dir->__d_position)
2121 if (!::readdir (dir))
2126 fhandler_disk_file::rewinddir (DIR *dir)
2128 d_cachepos (dir) = 0;
2129 if (wincap.has_buggy_restart_scan () && isremote ())
2131 /* This works around a W2K bug. The RestartScan parameter in calls
2132 to NtQueryDirectoryFile on remote shares is ignored, thus
2133 resulting in not being able to rewind on remote shares. By
2134 reopening the directory, we get a fresh new directory pointer. */
2135 OBJECT_ATTRIBUTES attr;
2140 pc.init_reopen_attr (&attr, get_handle ());
2141 status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
2142 &attr, &io, FILE_SHARE_VALID_FLAGS,
2143 FILE_SYNCHRONOUS_IO_NONALERT
2144 | FILE_OPEN_FOR_BACKUP_INTENT
2145 | FILE_DIRECTORY_FILE);
2146 if (!NT_SUCCESS (stat))
2147 debug_printf ("Unable to reopen dir %s, NT error: %p",
2148 get_name (), status);
2151 NtClose (get_handle ());
2152 set_io_handle (new_dir);
2155 dir->__d_position = 0;
2156 d_mounts (dir)->rewind ();
2160 fhandler_disk_file::closedir (DIR *dir)
2165 delete d_mounts (dir);
2168 else if (get_handle () == INVALID_HANDLE_VALUE)
2173 else if (!NT_SUCCESS (status = NtClose (get_handle ())))
2175 __seterrno_from_nt_status (status);
2178 syscall_printf ("%d = closedir (%p, %s)", res, dir, get_name ());
2182 fhandler_cygdrive::fhandler_cygdrive () :
2183 fhandler_disk_file (), ndrives (0), pdrive (NULL)
2188 fhandler_cygdrive::open (int flags, mode_t mode)
2190 if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
2195 if (flags & O_WRONLY)
2207 fhandler_cygdrive::close ()
2213 fhandler_cygdrive::set_drives ()
2215 pdrive = pdrive_buf;
2216 ndrives = GetLogicalDriveStrings (sizeof pdrive_buf, pdrive_buf) / DRVSZ;
2220 fhandler_cygdrive::fstat (struct __stat64 *buf)
2222 fhandler_base::fstat (buf);
2224 buf->st_mode = S_IFDIR | STD_RBITS | STD_XBITS;
2227 char flptst[] = "X:";
2229 for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
2230 if (is_floppy ((flptst[0] = *p, flptst))
2231 || GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
2233 buf->st_nlink = n + 2;
2238 fhandler_cygdrive::opendir (int fd)
2242 dir = fhandler_disk_file::opendir (fd);
2243 if (dir && !ndrives)
2250 fhandler_cygdrive::readdir (DIR *dir, dirent *de)
2252 char flptst[] = "X:";
2256 if (!pdrive || !*pdrive)
2258 if (!(dir->__flags & dirent_saw_dot))
2260 de->d_name[0] = '.';
2261 de->d_name[1] = '\0';
2266 if (!is_floppy ((flptst[0] = *pdrive, flptst))
2267 && GetFileAttributes (pdrive) != INVALID_FILE_ATTRIBUTES)
2269 pdrive = strchr (pdrive, '\0') + 1;
2271 *de->d_name = cyg_tolower (*pdrive);
2272 de->d_name[1] = '\0';
2273 user_shared->warned_msdos = true;
2274 de->d_ino = readdir_get_ino (pdrive, false);
2275 dir->__d_position++;
2276 pdrive = strchr (pdrive, '\0') + 1;
2277 syscall_printf ("%p = readdir (%p) (%s)", &de, dir, de->d_name);
2282 fhandler_cygdrive::rewinddir (DIR *dir)
2284 pdrive = pdrive_buf;
2285 dir->__d_position = 0;
2289 fhandler_cygdrive::closedir (DIR *dir)
2291 pdrive = pdrive_buf;