/* fhandler_disk_file.cc
Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005, 2006, 2007, 2008 Red Hat, Inc.
+ 2005, 2006, 2007, 2008, 2009, 2010, 2011 Red Hat, Inc.
This file is part of Cygwin.
#include "pinfo.h"
#include "ntdll.h"
#include "tls_pbuf.h"
+#include "pwdgrp.h"
#include <winioctl.h>
#define _COMPILING_NEWLIB
{
if (parent_dir_len == 1) /* root dir */
{
- UNICODE_STRING proc;
-
- RtlInitUnicodeString (&proc, L"proc");
- if (RtlEqualUnicodeString (fname, &proc, TRUE))
+ if (RtlEqualUnicodeString (fname, &ro_u_proc, FALSE))
{
found[__DIR_PROC] = true;
return 2;
}
if (fname->Length / sizeof (WCHAR) == mount_table->cygdrive_len - 2
- && RtlEqualUnicodeString (fname, &cygdrive, TRUE))
+ && RtlEqualUnicodeString (fname, &cygdrive, FALSE))
{
found[__DIR_CYGDRIVE] = true;
return 2;
}
}
for (int i = 0; i < count; ++i)
- if (RtlEqualUnicodeString (fname, &mounts[i], TRUE))
+ if (RtlEqualUnicodeString (fname, &mounts[i], FALSE))
{
found[i] = true;
return eval ? eval_ino (i) : 1;
{
found[__DIR_PROC] = true;
if (retname)
- RtlInitUnicodeString (retname, L"proc");
+ *retname = ro_u_proc;
return 2;
}
if (!found[__DIR_CYGDRIVE])
void rewind () { memset (found, 0, sizeof found); }
};
-static inline bool
-is_volume_mountpoint (POBJECT_ATTRIBUTES attr)
+inline bool
+path_conv::isgood_inode (__ino64_t ino) const
+{
+ /* We can't trust remote inode numbers of only 32 bit. That means,
+ all remote inode numbers when running under NT4, as well as remote NT4
+ NTFS, as well as shares of Samba version < 3.0.
+ The known exception are SFU NFS shares, which return the valid 32 bit
+ inode number from the remote file system unchanged. */
+ return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ());
+}
+
+/* Check reparse point for type. IO_REPARSE_TAG_MOUNT_POINT types are
+ either volume mount points, which are treated as directories, or they
+ are directory mount points, which are treated as symlinks.
+ IO_REPARSE_TAG_SYMLINK types are always symlinks. We don't know
+ anything about other reparse points, so they are treated as unknown. */
+static inline int
+readdir_check_reparse_point (POBJECT_ATTRIBUTES attr)
{
- bool ret = false;
+ DWORD ret = DT_UNKNOWN;
IO_STATUS_BLOCK io;
HANDLE reph;
+ UNICODE_STRING subst;
if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, attr, &io,
FILE_SHARE_VALID_FLAGS,
alloca (MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
if (NT_SUCCESS (NtFsControlFile (reph, NULL, NULL, NULL,
&io, FSCTL_GET_REPARSE_POINT, NULL, 0,
- (LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE))
- && rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT
- && rp->SymbolicLinkReparseBuffer.PrintNameLength == 0)
- ret = true;
- NtClose (reph);
+ (LPVOID) rp, MAXIMUM_REPARSE_DATA_BUFFER_SIZE)))
+ {
+ if (rp->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
+ {
+ RtlInitCountedUnicodeString (&subst,
+ (WCHAR *)((char *)rp->MountPointReparseBuffer.PathBuffer
+ + rp->MountPointReparseBuffer.SubstituteNameOffset),
+ rp->MountPointReparseBuffer.SubstituteNameLength);
+ /* Only volume mountpoints are treated as directories. */
+ if (RtlEqualUnicodePathPrefix (&subst, &ro_u_volume, TRUE))
+ ret = DT_DIR;
+ else
+ ret = DT_LNK;
+ }
+ else if (rp->ReparseTag == IO_REPARSE_TAG_SYMLINK)
+ ret = DT_LNK;
+ NtClose (reph);
+ }
}
return ret;
}
-static inline __ino64_t
-get_ino_by_handle (HANDLE hdl)
+inline __ino64_t
+path_conv::get_ino_by_handle (HANDLE hdl)
{
IO_STATUS_BLOCK io;
- FILE_INTERNAL_INFORMATION pfai;
+ FILE_INTERNAL_INFORMATION fai;
- if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &pfai, sizeof pfai,
- FileInternalInformation)))
- return pfai.FileId.QuadPart;
+ if (NT_SUCCESS (NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
+ FileInternalInformation))
+ && isgood_inode (fai.FileId.QuadPart))
+ return fai.FileId.QuadPart;
return 0;
}
+#if 0
+/* This function is obsolete. We're keeping it in so we don't forget
+ that we already did all that at one point. */
unsigned __stdcall
path_conv::ndisk_links (DWORD nNumberOfLinks)
{
unsigned count = 0;
bool first = true;
- PFILE_DIRECTORY_INFORMATION fdibuf = (PFILE_DIRECTORY_INFORMATION)
+ PFILE_BOTH_DIRECTORY_INFORMATION fdibuf = (PFILE_BOTH_DIRECTORY_INFORMATION)
alloca (65536);
__DIR_mounts *dir = new __DIR_mounts (normalized_path);
- while (NT_SUCCESS (NtQueryDirectoryFile (fh, NULL, NULL, 0, &io, fdibuf,
- 65536, FileDirectoryInformation,
+ while (NT_SUCCESS (NtQueryDirectoryFile (fh, NULL, NULL, NULL, &io, fdibuf,
+ 65536, FileBothDirectoryInformation,
FALSE, NULL, first)))
{
if (first)
if (fdibuf->FileNameLength != 2 || fdibuf->FileName[0] != L'.')
count = 2;
}
- for (PFILE_DIRECTORY_INFORMATION pfdi = fdibuf;
+ for (PFILE_BOTH_DIRECTORY_INFORMATION pfdi = fdibuf;
pfdi;
- pfdi = (PFILE_DIRECTORY_INFORMATION)
+ pfdi = (PFILE_BOTH_DIRECTORY_INFORMATION)
(pfdi->NextEntryOffset ? (PBYTE) pfdi + pfdi->NextEntryOffset
: NULL))
{
RtlInitCountedUnicodeString (&fname, pfdi->FileName,
pfdi->FileNameLength);
InitializeObjectAttributes (&attr, &fname,
- OBJ_CASE_INSENSITIVE, fh, NULL);
+ objcaseinsensitive (), fh, NULL);
if (is_volume_mountpoint (&attr))
++count;
}
delete dir;
return count;
}
+#endif
-inline bool
-path_conv::isgood_inode (__ino64_t ino) const
+/* For files on NFS shares, we request an EA of type NfsV3Attributes.
+ This returns the content of a struct fattr3 as defined in RFC 1813.
+ The content is the NFS equivalent of struct stat. so there's not much
+ to do here except for copying. */
+int __stdcall
+fhandler_base::fstat_by_nfs_ea (struct __stat64 *buf)
{
- /* We can't trust remote inode numbers of only 32 bit. That means,
- all remote inode numbers when running under NT4, as well as remote NT4
- NTFS, as well as shares of Samba version < 3.0.
- The known exception are SFU NFS shares, which return the valid 32 bit
- inode number from the remote file system unchanged. */
- return hasgood_inode () && (ino > UINT32_MAX || !isremote () || fs_is_nfs ());
+ fattr3 *nfs_attr = pc.nfsattr ();
+
+ if (get_io_handle ())
+ {
+ /* NFS stumbles over its own caching. If you write to the file,
+ a subsequent fstat does not return the actual size of the file,
+ but the size at the time the handle has been opened. Unless
+ access through another handle invalidates the caching within the
+ NFS client. */
+ if (get_access () & GENERIC_WRITE)
+ FlushFileBuffers (get_io_handle ());
+ nfs_fetch_fattr3 (get_io_handle (), nfs_attr);
+ }
+ buf->st_dev = nfs_attr->fsid;
+ buf->st_ino = nfs_attr->fileid;
+ buf->st_mode = (nfs_attr->mode & 0xfff)
+ | nfs_type_mapping[nfs_attr->type & 7];
+ buf->st_nlink = nfs_attr->nlink;
+ /* FIXME: How to convert UNIX uid/gid to Windows SIDs? */
+#if 0
+ buf->st_uid = nfs_attr->uid;
+ buf->st_gid = nfs_attr->gid;
+#else
+ buf->st_uid = myself->uid;
+ buf->st_gid = myself->gid;
+#endif
+ buf->st_rdev = makedev (nfs_attr->rdev.specdata1,
+ nfs_attr->rdev.specdata2);
+ buf->st_size = nfs_attr->size;
+ buf->st_blksize = PREFERRED_IO_BLKSIZE;
+ buf->st_blocks = (nfs_attr->used + S_BLKSIZE - 1) / S_BLKSIZE;
+ buf->st_atim = nfs_attr->atime;
+ buf->st_mtim = nfs_attr->mtime;
+ buf->st_ctim = nfs_attr->ctime;
+ return 0;
}
int __stdcall
fhandler_base::fstat_by_handle (struct __stat64 *buf)
{
- NTSTATUS status;
+ /* Don't use FileAllInformation info class. It returns a pathname rather
+ than a filename, so it needs a really big buffer for no good reason
+ since we don't need the name anyway. So we just call the three info
+ classes necessary to get all information required by stat(2). */
+ FILE_STANDARD_INFORMATION fsi;
+ FILE_INTERNAL_INFORMATION fii;
+
+ HANDLE h = get_stat_handle ();
+ NTSTATUS status = 0;
IO_STATUS_BLOCK io;
- /* The entries potentially contain a name of MAX_PATH wide characters. */
- const DWORD fai_size = (NAME_MAX + 1) * sizeof (WCHAR)
- + sizeof (FILE_ALL_INFORMATION);
- PFILE_ALL_INFORMATION pfai = (PFILE_ALL_INFORMATION) alloca (fai_size);
- status = NtQueryInformationFile (get_handle (), &io, pfai, fai_size,
- FileAllInformation);
- if (NT_SUCCESS (status))
+ /* If the file has been opened for other purposes than stat, we can't rely
+ on the information stored in pc.fnoi. So we overwrite them here. */
+ if (get_io_handle ())
{
- /* If the change time is 0, it's a file system which doesn't
- support a change timestamp. In that case use the LastWriteTime
- entry, as in other calls to fstat_helper. */
- if (pc.is_rep_symlink ())
- pfai->BasicInformation.FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
- pc.file_attributes (pfai->BasicInformation.FileAttributes);
- return fstat_helper (buf,
- pfai->BasicInformation.ChangeTime.QuadPart
- ? *(FILETIME *) &pfai->BasicInformation.ChangeTime
- : *(FILETIME *) &pfai->BasicInformation.LastWriteTime,
- *(FILETIME *) &pfai->BasicInformation.LastAccessTime,
- *(FILETIME *) &pfai->BasicInformation.LastWriteTime,
- *(FILETIME *) &pfai->BasicInformation.CreationTime,
- get_dev (),
- pfai->StandardInformation.EndOfFile.QuadPart,
- pfai->StandardInformation.AllocationSize.QuadPart,
- pfai->InternalInformation.FileId.QuadPart,
- pfai->StandardInformation.NumberOfLinks,
- pfai->BasicInformation.FileAttributes);
- }
- debug_printf ("%p = NtQueryInformationFile(%S)",
- status, pc.get_nt_native_path ());
- return -1;
+ PFILE_NETWORK_OPEN_INFORMATION pfnoi = pc.fnoi ();
+ status = NtQueryInformationFile (h, &io, pfnoi, sizeof *pfnoi,
+ FileNetworkOpenInformation);
+ if (!NT_SUCCESS (status))
+ {
+ debug_printf ("%p = NtQueryInformationFile(%S, "
+ "FileNetworkOpenInformation)",
+ status, pc.get_nt_native_path ());
+ return -1;
+ }
+ }
+ if (!pc.hasgood_inode ())
+ fsi.NumberOfLinks = 1;
+ else
+ {
+ status = NtQueryInformationFile (h, &io, &fsi, sizeof fsi,
+ FileStandardInformation);
+ if (!NT_SUCCESS (status))
+ {
+ debug_printf ("%p = NtQueryInformationFile(%S, "
+ "FileStandardInformation)",
+ status, pc.get_nt_native_path ());
+ return -1;
+ }
+ if (!ino)
+ {
+ status = NtQueryInformationFile (h, &io, &fii, sizeof fii,
+ FileInternalInformation);
+ if (!NT_SUCCESS (status))
+ {
+ debug_printf ("%p = NtQueryInformationFile(%S, "
+ "FileInternalInformation)",
+ status, pc.get_nt_native_path ());
+ return -1;
+ }
+ ino = fii.FileId.QuadPart;
+ }
+ }
+ return fstat_helper (buf, fsi.NumberOfLinks);
}
int __stdcall
UNICODE_STRING dirname;
UNICODE_STRING basename;
HANDLE dir;
- const DWORD fdi_size = (NAME_MAX + 1) * sizeof (WCHAR)
- + sizeof (FILE_ID_BOTH_DIR_INFORMATION);
- const DWORD fvi_size = (NAME_MAX + 1) * sizeof (WCHAR)
- + sizeof (FILE_FS_VOLUME_INFORMATION);
- PFILE_ID_BOTH_DIR_INFORMATION pfdi = (PFILE_ID_BOTH_DIR_INFORMATION)
- alloca (fdi_size);
- PFILE_FS_VOLUME_INFORMATION pfvi = (PFILE_FS_VOLUME_INFORMATION)
- alloca (fvi_size);
- LARGE_INTEGER FileId;
-
- if (!pc.exists ())
- {
- debug_printf ("already determined that pc does not exist");
- set_errno (ENOENT);
- return -1;
- }
- RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
- InitializeObjectAttributes (&attr, &dirname, OBJ_CASE_INSENSITIVE,
- NULL, NULL);
- if (!NT_SUCCESS (status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
- &attr, &io, FILE_SHARE_VALID_FLAGS,
- FILE_SYNCHRONOUS_IO_NONALERT
- | FILE_OPEN_FOR_BACKUP_INTENT
- | FILE_DIRECTORY_FILE)))
- {
- debug_printf ("%p = NtOpenFile(%S)", status, pc.get_nt_native_path ());
- goto too_bad;
- }
- if (wincap.has_fileid_dirinfo ()
- && NT_SUCCESS (status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
- pfdi, fdi_size,
- FileIdBothDirectoryInformation,
- TRUE, &basename, TRUE)))
- FileId = pfdi->FileId;
- else if (NT_SUCCESS (status = NtQueryDirectoryFile (dir, NULL, NULL, 0, &io,
- pfdi, fdi_size,
- FileBothDirectoryInformation,
- TRUE, &basename, TRUE)))
- FileId.QuadPart = 0; /* get_ino is called in fstat_helper. */
- if (!NT_SUCCESS (status))
- {
- debug_printf ("%p = NtQueryDirectoryFile(%S)", status,
- pc.get_nt_native_path ());
- NtClose (dir);
- goto too_bad;
+ struct {
+ FILE_ID_BOTH_DIR_INFORMATION fdi;
+ WCHAR buf[NAME_MAX + 1];
+ } fdi_buf;
+
+ if (!ino && pc.hasgood_inode ()
+ && wincap.has_fileid_dirinfo () && !pc.has_buggy_fileid_dirinfo ())
+ {
+ RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, &basename);
+ InitializeObjectAttributes (&attr, &dirname, pc.objcaseinsensitive (),
+ NULL, NULL);
+ status = NtOpenFile (&dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ &attr, &io, FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ | FILE_OPEN_FOR_BACKUP_INTENT
+ | FILE_DIRECTORY_FILE);
+ if (!NT_SUCCESS (status))
+ debug_printf ("%p = NtOpenFile(%S)", status,
+ pc.get_nt_native_path ());
+ else
+ {
+ status = NtQueryDirectoryFile (dir, NULL, NULL, NULL, &io,
+ &fdi_buf.fdi, sizeof fdi_buf,
+ FileIdBothDirectoryInformation,
+ TRUE, &basename, TRUE);
+ NtClose (dir);
+ if (!NT_SUCCESS (status))
+ debug_printf ("%p = NtQueryDirectoryFile(%S)", status,
+ pc.get_nt_native_path ());
+ else
+ ino = fdi_buf.fdi.FileId.QuadPart;
+ }
}
- status = NtQueryVolumeInformationFile (dir, &io, pfvi, fvi_size,
- FileFsVolumeInformation);
- if (!NT_SUCCESS (status))
- {
- debug_printf ("%p = NtQueryVolumeInformationFile(%S)",
- status, pc.get_nt_native_path ());
- pfvi->VolumeSerialNumber = 0;
- }
- NtClose (dir);
- /* If the change time is 0, it's a file system which doesn't
- support a change timestamp. In that case use the LastWriteTime
- entry, as in other calls to fstat_helper. */
- if (pc.is_rep_symlink ())
- pfdi->FileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
- pc.file_attributes (pfdi->FileAttributes);
- return fstat_helper (buf,
- pfdi->ChangeTime.QuadPart ?
- *(FILETIME *) &pfdi->ChangeTime :
- *(FILETIME *) &pfdi->LastWriteTime,
- *(FILETIME *) &pfdi->LastAccessTime,
- *(FILETIME *) &pfdi->LastWriteTime,
- *(FILETIME *) &pfdi->CreationTime,
- pfvi->VolumeSerialNumber,
- pfdi->EndOfFile.QuadPart,
- pfdi->AllocationSize.QuadPart,
- pfdi->FileId.QuadPart,
- 1,
- pfdi->FileAttributes);
-
-too_bad:
- LARGE_INTEGER ft;
- /* Arbitrary value: 2006-12-01 */
- RtlSecondsSince1970ToTime (1164931200L, &ft);
- return fstat_helper (buf,
- *(FILETIME *) &ft,
- *(FILETIME *) &ft,
- *(FILETIME *) &ft,
- *(FILETIME *) &ft,
- 0,
- 0ULL,
- -1LL,
- 0ULL,
- 1,
- pc.file_attributes ());
+ return fstat_helper (buf, 1);
}
int __stdcall
int oret;
int open_flags = O_RDONLY | O_BINARY;
- if (get_handle ())
+ if (get_stat_handle ())
{
if (!nohandle () && !is_fs_special ())
- res = fstat_by_handle (buf);
+ res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
if (res)
res = fstat_by_name (buf);
return res;
}
- query_open (query_read_attributes);
+ /* First try to open with generic read access. This allows to read the file
+ in fstat_helper (when checking for executability) without having to
+ re-open it. Opening a file can take a lot of time on network drives
+ so we try to avoid that. */
oret = open_fs (open_flags, 0);
+ if (!oret)
+ {
+ query_open (query_read_attributes);
+ oret = open_fs (open_flags, 0);
+ }
if (oret)
{
/* We now have a valid handle, regardless of the "nohandle" state.
Since fhandler_base::open only calls CloseHandle if !nohandle,
we have to set it to false before calling close and restore
the state afterwards. */
- res = fstat_by_handle (buf);
+ res = pc.fs_is_nfs () ? fstat_by_nfs_ea (buf) : fstat_by_handle (buf);
bool no_handle = nohandle ();
nohandle (false);
close_fs ();
return res;
}
-/* The ftChangeTime is taken from the NTFS ChangeTime entry, if reading
- the file information using NtQueryInformationFile succeeded. If not,
- it's faked using the LastWriteTime entry from GetFileInformationByHandle
- or FindFirstFile. We're deliberatly not using the creation time anymore
- to simplify interaction with native Windows applications which choke on
- creation times >= access or write times.
-
- Note that the dwFileAttributes member of the file information evaluated
- in the calling function is used here, not the pc.fileattr member, since
- the latter might be old and not reflect the actual state of the file. */
int __stdcall
fhandler_base::fstat_helper (struct __stat64 *buf,
- FILETIME ftChangeTime,
- FILETIME ftLastAccessTime,
- FILETIME ftLastWriteTime,
- FILETIME ftCreationTime,
- DWORD dwVolumeSerialNumber,
- ULONGLONG nFileSize,
- LONGLONG nAllocSize,
- ULONGLONG nFileIndex,
- DWORD nNumberOfLinks,
- DWORD dwFileAttributes)
+ DWORD nNumberOfLinks)
{
IO_STATUS_BLOCK st;
FILE_COMPRESSION_INFORMATION fci;
-
- to_timestruc_t (&ftLastAccessTime, &buf->st_atim);
- to_timestruc_t (&ftLastWriteTime, &buf->st_mtim);
- to_timestruc_t (&ftChangeTime, &buf->st_ctim);
- to_timestruc_t (&ftCreationTime, &buf->st_birthtim);
- buf->st_dev = dwVolumeSerialNumber;
- buf->st_size = (_off64_t) nFileSize;
- /* The number of links to a directory includes the
- number of subdirectories in the directory, since all
- those subdirectories point to it.
- This is too slow on remote drives, so we do without it.
- Setting the count to 2 confuses `find (1)' command. So
- let's try it with `1' as link count. */
+ HANDLE h = get_stat_handle ();
+ PFILE_NETWORK_OPEN_INFORMATION pfnoi = pc.fnoi ();
+ ULONG attributes = pc.file_attributes ();
+
+ to_timestruc_t ((PFILETIME) &pfnoi->LastAccessTime, &buf->st_atim);
+ to_timestruc_t ((PFILETIME) &pfnoi->LastWriteTime, &buf->st_mtim);
+ /* If the ChangeTime is 0, the underlying FS doesn't support this timestamp
+ (FAT for instance). If so, it's faked using LastWriteTime. */
+ to_timestruc_t (pfnoi->ChangeTime.QuadPart ? (PFILETIME) &pfnoi->ChangeTime
+ : (PFILETIME) &pfnoi->LastWriteTime,
+ &buf->st_ctim);
+ to_timestruc_t ((PFILETIME) &pfnoi->CreationTime, &buf->st_birthtim);
+ buf->st_rdev = buf->st_dev = get_dev ();
+ /* CV 2011-01-13: Observations on the Cygwin mailing list point to an
+ interesting behaviour in some Windows versions. Apparently the size of
+ a directory is computed at the time the directory is first scanned. This
+ can result in two subsequent NtQueryInformationFile calls to return size
+ 0 in the first call and size > 0 in the second call. This in turn can
+ affect applications like newer tar.
+ FIXME: Is the allocation size affected as well? */
+ buf->st_size = pc.isdir () ? 0 : (_off64_t) pfnoi->EndOfFile.QuadPart;
+ /* The number of links to a directory includes the number of subdirectories
+ in the directory, since all those subdirectories point to it. However,
+ this is painfully slow, so we do without it. */
+#if 0
buf->st_nlink = pc.ndisk_links (nNumberOfLinks);
+#else
+ buf->st_nlink = nNumberOfLinks;
+#endif
/* Enforce namehash as inode number on untrusted file systems. */
- if (pc.isgood_inode (nFileIndex))
- buf->st_ino = (__ino64_t) nFileIndex;
+ if (ino && pc.isgood_inode (ino))
+ buf->st_ino = (__ino64_t) ino;
else
buf->st_ino = get_ino ();
buf->st_blksize = PREFERRED_IO_BLKSIZE;
- if (nAllocSize >= 0LL)
+ if (pfnoi->AllocationSize.QuadPart >= 0LL)
/* A successful NtQueryInformationFile returns the allocation size
correctly for compressed and sparse files as well. */
- buf->st_blocks = (nAllocSize + S_BLKSIZE - 1) / S_BLKSIZE;
- else if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_COMPRESSED
- | FILE_ATTRIBUTE_SPARSE_FILE)
- && get_handle () && !is_fs_special ()
- && !NtQueryInformationFile (get_handle (), &st, (PVOID) &fci,
- sizeof fci, FileCompressionInformation))
+ buf->st_blocks = (pfnoi->AllocationSize.QuadPart + S_BLKSIZE - 1)
+ / S_BLKSIZE;
+ else if (::has_attribute (attributes, FILE_ATTRIBUTE_COMPRESSED
+ | FILE_ATTRIBUTE_SPARSE_FILE)
+ && h && !is_fs_special ()
+ && !NtQueryInformationFile (h, &st, (PVOID) &fci, sizeof fci,
+ FileCompressionInformation))
/* Otherwise we request the actual amount of bytes allocated for
compressed and sparsed files. */
buf->st_blocks = (fci.CompressedFileSize.QuadPart + S_BLKSIZE - 1)
/ S_BLKSIZE;
else
/* Otherwise compute no. of blocks from file size. */
- buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
+ buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
buf->st_mode = 0;
- /* Using a side effect: get_file_attibutes checks for
- directory. This is used, to set S_ISVTX, if needed. */
+ /* Using a side effect: get_file_attributes checks for directory.
+ This is used, to set S_ISVTX, if needed. */
if (pc.isdir ())
buf->st_mode = S_IFDIR;
else if (pc.issymlink ())
buf->st_size = pc.get_symlink_length ();
/* symlinks are everything for everyone! */
buf->st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
- get_file_attribute (get_handle (), pc, NULL,
+ get_file_attribute (h, pc, NULL,
&buf->st_uid, &buf->st_gid);
goto done;
}
else if (pc.issocket ())
buf->st_mode = S_IFSOCK;
- if (!get_file_attribute (is_fs_special () && !pc.issocket ()
- ? NULL : get_handle (), pc,
+ if (!get_file_attribute (is_fs_special () && !pc.issocket () ? NULL : h, pc,
&buf->st_mode, &buf->st_uid, &buf->st_gid))
{
/* If read-only attribute is set, modify ntsec return value */
- if (::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY)
+ if (::has_attribute (attributes, FILE_ATTRIBUTE_READONLY)
&& !pc.isdir () && !pc.issymlink ())
buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
buf->st_mode |= S_IFREG;
else
{
- buf->st_dev = dev ();
+ buf->st_dev = buf->st_rdev = dev ();
buf->st_mode = dev ().mode;
buf->st_size = 0;
}
{
buf->st_mode |= STD_RBITS;
- if (!::has_attribute (dwFileAttributes, FILE_ATTRIBUTE_READONLY))
+ if (!::has_attribute (attributes, FILE_ATTRIBUTE_READONLY))
buf->st_mode |= STD_WBITS;
/* | S_IWGRP | S_IWOTH; we don't give write to group etc */
/* nothing */;
else if (is_fs_special ())
{
- buf->st_dev = dev ();
+ buf->st_dev = buf->st_rdev = dev ();
buf->st_mode = dev ().mode;
buf->st_size = 0;
}
else
{
buf->st_mode |= S_IFREG;
+ /* Check suffix for executable file. */
+ if (pc.exec_state () != is_executable)
+ {
+ PUNICODE_STRING path = pc.get_nt_native_path ();
+
+ if (RtlEqualUnicodePathSuffix (path, &ro_u_exe, TRUE)
+ || RtlEqualUnicodePathSuffix (path, &ro_u_lnk, TRUE)
+ || RtlEqualUnicodePathSuffix (path, &ro_u_com, TRUE))
+ pc.set_exec ();
+ }
+ /* No known suffix, check file header. This catches binaries and
+ shebang scripts. */
if (pc.exec_state () == dont_know_if_executable)
{
- UNICODE_STRING same;
OBJECT_ATTRIBUTES attr;
- HANDLE h;
+ NTSTATUS status = 0;
IO_STATUS_BLOCK io;
- RtlInitUnicodeString (&same, L"");
- InitializeObjectAttributes (&attr, &same, 0, get_handle (), NULL);
- if (NT_SUCCESS (NtOpenFile (&h, FILE_READ_DATA, &attr, &io,
- FILE_SHARE_VALID_FLAGS, 0)))
+ /* We have to re-open the file. Either the file is not opened
+ for reading, or the read will change the file position of the
+ original handle. */
+ pc.init_reopen_attr (&attr, h);
+ status = NtOpenFile (&h, SYNCHRONIZE | FILE_READ_DATA,
+ &attr, &io, FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT
+ | FILE_SYNCHRONOUS_IO_NONALERT);
+ if (!NT_SUCCESS (status))
+ debug_printf ("%p = NtOpenFile(%S)", status,
+ pc.get_nt_native_path ());
+ else
{
LARGE_INTEGER off = { QuadPart:0LL };
char magic[3];
- if (NT_SUCCESS (NtReadFile (h, NULL, NULL, NULL, &io, magic,
- 3, &off, NULL))
- && has_exec_chars (magic, io.Information))
+ status = NtReadFile (h, NULL, NULL, NULL,
+ &io, magic, 3, &off, NULL);
+ if (!NT_SUCCESS (status))
+ debug_printf ("%p = NtReadFile(%S)", status,
+ pc.get_nt_native_path ());
+ else if (has_exec_chars (magic, io.Information))
{
+ /* Heureka, it's an executable */
pc.set_exec ();
buf->st_mode |= STD_XBITS;
}
/* This fakes the permissions of all files to match the current umask. */
buf->st_mode &= ~(cygheap->umask);
+ /* If the FS supports ACLs, we're here because we couldn't even open
+ the file for READ_CONTROL access. Chances are high that the file's
+ security descriptor has no ACE for "Everyone", so we should not fake
+ any access for "others". */
+ if (has_acls ())
+ buf->st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH);
}
done:
- syscall_printf ("0 = fstat (, %p) st_atime=%x st_size=%D, st_mode=%p, st_ino=%D, sizeof=%d",
- buf, buf->st_atime, buf->st_size, buf->st_mode,
- buf->st_ino, sizeof (*buf));
+ syscall_printf ("0 = fstat (%S, %p) st_atime=%x st_size=%D, st_mode=%p, "
+ "st_ino=%D, sizeof=%d",
+ pc.get_nt_native_path (), buf, buf->st_atime, buf->st_size,
+ buf->st_mode, buf->st_ino, sizeof (*buf));
return 0;
}
int ret = -1, opened = 0;
NTSTATUS status;
IO_STATUS_BLOCK io;
- const size_t fvi_size = sizeof (FILE_FS_VOLUME_INFORMATION)
- + (NAME_MAX + 1) * sizeof (WCHAR);
- PFILE_FS_VOLUME_INFORMATION pfvi = (PFILE_FS_VOLUME_INFORMATION)
- alloca (fvi_size);
- const size_t fai_size = sizeof (FILE_FS_ATTRIBUTE_INFORMATION)
- + (NAME_MAX + 1) * sizeof (WCHAR);
- PFILE_FS_ATTRIBUTE_INFORMATION pfai = (PFILE_FS_ATTRIBUTE_INFORMATION)
- alloca (fai_size);
FILE_FS_FULL_SIZE_INFORMATION full_fsi;
FILE_FS_SIZE_INFORMATION fsi;
+ /* We must not use the stat handle here, even if it exists. The handle
+ has been opened with FILE_OPEN_REPARSE_POINT, thus, in case of a volume
+ mount point, it points to the FS of the mount point, rather than to the
+ mounted FS. */
HANDLE fh = get_handle ();
if (!fh)
{
OBJECT_ATTRIBUTES attr;
opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL,
- pc.get_object_attr (attr, sec_none_nih),
- &io, FILE_SHARE_VALID_FLAGS,
- FILE_OPEN_FOR_BACKUP_INTENT));
+ pc.get_object_attr (attr, sec_none_nih),
+ &io, FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT));
if (!opened)
{
/* Can't open file. Try again with parent dir. */
RtlSplitUnicodePath (pc.get_nt_native_path (), &dirname, NULL);
attr.ObjectName = &dirname;
opened = NT_SUCCESS (NtOpenFile (&fh, READ_CONTROL, &attr, &io,
- FILE_SHARE_VALID_FLAGS,
- FILE_OPEN_FOR_BACKUP_INTENT));
+ FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT));
if (!opened)
goto out;
}
}
- /* Get basic volume information. */
- status = NtQueryVolumeInformationFile (fh, &io, pfvi, fvi_size,
- FileFsVolumeInformation);
- if (!NT_SUCCESS (status))
- {
- __seterrno_from_nt_status (status);
- goto out;
- }
- status = NtQueryVolumeInformationFile (fh, &io, pfai, fai_size,
- FileFsAttributeInformation);
- if (!NT_SUCCESS (status))
- {
- __seterrno_from_nt_status (status);
- goto out;
- }
sfs->f_files = ULONG_MAX;
sfs->f_ffree = ULONG_MAX;
sfs->f_favail = ULONG_MAX;
- sfs->f_fsid = pfvi->VolumeSerialNumber;
- sfs->f_flag = pfai->FileSystemAttributes;
- sfs->f_namemax = pfai->MaximumComponentNameLength;
+ sfs->f_fsid = pc.fs_serial_number ();
+ sfs->f_flag = pc.fs_flags ();
+ sfs->f_namemax = pc.fs_name_len ();
/* Get allocation related information. Try to get "full" information
first, which is only available since W2K. If that fails, try to
retrieve normal allocation information. */
extern int chmod_device (path_conv& pc, mode_t mode);
int res = -1;
int oret = 0;
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
if (pc.is_fs_special ())
return chmod_device (pc, mode);
if (!get_handle ())
{
- query_open (query_write_control);
+ query_open (query_write_dac);
if (!(oret = open (O_BINARY, 0)))
{
/* Need WRITE_DAC|WRITE_OWNER to write ACLs. */
- if (allow_ntsec && pc.has_acls ())
+ if (pc.has_acls ())
return -1;
/* Otherwise FILE_WRITE_ATTRIBUTES is sufficient. */
query_open (query_write_attributes);
}
}
- if (allow_ntsec && pc.has_acls ())
+ if (pc.fs_is_nfs ())
+ {
+ /* chmod on NFS shares works by writing an EA of type NfsV3Attributes.
+ Only type and mode have to be set. Apparently type isn't checked
+ for consistency, so it's sufficent to set it to NF3REG all the time. */
+ struct {
+ FILE_FULL_EA_INFORMATION ffei;
+ char buf[sizeof (NFS_V3_ATTR) + sizeof (fattr3)];
+ } ffei_buf;
+ ffei_buf.ffei.NextEntryOffset = 0;
+ ffei_buf.ffei.Flags = 0;
+ ffei_buf.ffei.EaNameLength = sizeof (NFS_V3_ATTR) - 1;
+ ffei_buf.ffei.EaValueLength = sizeof (fattr3);
+ strcpy (ffei_buf.ffei.EaName, NFS_V3_ATTR);
+ fattr3 *nfs_attr = (fattr3 *) (ffei_buf.ffei.EaName
+ + ffei_buf.ffei.EaNameLength + 1);
+ memset (nfs_attr, 0, sizeof (fattr3));
+ nfs_attr->type = NF3REG;
+ nfs_attr->mode = mode;
+ status = NtSetEaFile (get_handle (), &io,
+ &ffei_buf.ffei, sizeof ffei_buf);
+ if (!NT_SUCCESS (status))
+ __seterrno_from_nt_status (status);
+ else
+ res = 0;
+ goto out;
+ }
+
+ if (pc.has_acls ())
{
if (pc.isdir ())
mode |= S_IFDIR;
if (!set_file_attribute (get_handle (), pc,
- ILLEGAL_UID, ILLEGAL_GID, mode)
- && allow_ntsec)
+ ILLEGAL_UID, ILLEGAL_GID, mode))
res = 0;
}
- /* if the mode we want has any write bits set, we can't be read only. */
+ /* If the mode has any write bits set, the DOS R/O flag is in the way. */
if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
pc &= (DWORD) ~FILE_ATTRIBUTE_READONLY;
- else
+ else if (!pc.has_acls ()) /* Never set DOS R/O if security is used. */
pc |= (DWORD) FILE_ATTRIBUTE_READONLY;
- if (mode & S_IFSOCK)
+ if (S_ISSOCK (mode))
pc |= (DWORD) FILE_ATTRIBUTE_SYSTEM;
- IO_STATUS_BLOCK io;
- FILE_BASIC_INFORMATION fbi;
- fbi.CreationTime.QuadPart = fbi.LastAccessTime.QuadPart
- = fbi.LastWriteTime.QuadPart = fbi.ChangeTime.QuadPart = 0LL;
- fbi.FileAttributes = pc.file_attributes () ?: FILE_ATTRIBUTE_NORMAL;
- NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
- FileBasicInformation);
- if (!NT_SUCCESS (status))
- __seterrno_from_nt_status (status);
- else if (!allow_ntsec || !pc.has_acls ())
- /* Correct NTFS security attributes have higher priority */
- res = 0;
+ status = NtSetAttributesFile (get_handle (), pc.file_attributes ());
+ /* MVFS needs a good amount of kicking to be convinced that it has to write
+ back metadata changes and to invalidate the cached metadata information
+ stored for the given handle. This method to open a second handle to
+ the file and write the same metadata information twice has been found
+ experimentally: http://cygwin.com/ml/cygwin/2009-07/msg00533.html */
+ if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !oret)
+ {
+ OBJECT_ATTRIBUTES attr;
+ HANDLE fh;
+
+ pc.init_reopen_attr (&attr, get_handle ());
+ if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT)))
+ {
+ NtSetAttributesFile (fh, pc.file_attributes ());
+ NtClose (fh);
+ }
+ }
+ /* Correct NTFS security attributes have higher priority */
+ if (!pc.has_acls ())
+ {
+ if (!NT_SUCCESS (status))
+ __seterrno_from_nt_status (status);
+ else
+ res = 0;
+ }
+out:
if (oret)
close_fs ();
{
int oret = 0;
- if (!pc.has_acls () || !allow_ntsec)
+ if (!pc.has_acls ())
{
/* fake - if not supported, pretend we're like win95
where it just works */
+ /* FIXME: Could be supported on NFS when user->uid mapping is in place. */
return 0;
}
mode_t attrib = 0;
if (pc.isdir ())
attrib |= S_IFDIR;
- int res = get_file_attribute (get_handle (), pc, &attrib, NULL, NULL);
+ __uid32_t old_uid;
+ int res = get_file_attribute (get_handle (), pc, &attrib, &old_uid, NULL);
if (!res)
{
/* Typical Windows default ACLs can contain permissions for one
if (pc.issymlink ())
attrib = S_IFLNK | STD_RBITS | STD_WBITS;
res = set_file_attribute (get_handle (), pc, uid, gid, attrib);
+ /* If you're running a Samba server which has no winbidd running, the
+ uid<->SID mapping is disfunctional. Even trying to chown to your
+ own account fails since the account used on the server is the UNIX
+ account which gets used for the standard user mapping. This is a
+ default mechanism which doesn't know your real Windows SID.
+ There are two possible error codes in different Samba releases for
+ this situation, one of them is unfortunately the not very significant
+ STATUS_ACCESS_DENIED. Instead of relying on the error codes, we're
+ using the below very simple heuristic. If set_file_attribute failed,
+ and the original user account was either already unknown, or one of
+ the standard UNIX accounts, we're faking success. */
+ if (res == -1 && pc.fs_is_samba ())
+ {
+ cygsid sid;
+
+ if (old_uid == ILLEGAL_UID
+ || (sid.getfrompw (internal_getpwuid (old_uid))
+ && EqualPrefixSid (sid, well_known_samba_unix_user_fake_sid)))
+ {
+ debug_printf ("Faking chown worked on standalone Samba");
+ res = 0;
+ }
+ }
}
if (oret)
close_fs ();
int res = -1;
int oret = 0;
- if (!pc.has_acls () || !allow_ntsec)
+ if (!pc.has_acls ())
{
cant_access_acl:
switch (cmd)
set_errno (ENOSPC);
else
{
- if (!get_handle ())
- {
- query_open (query_read_attributes);
- oret = open (O_BINARY, 0);
- }
- if ((!oret && !fstat_by_handle (&st))
- || !fstat_by_name (&st))
+ if (!fstat (&st))
{
aclbufp[0].a_type = USER_OBJ;
aclbufp[0].a_id = st.st_uid;
}
else
{
- if (!get_handle ())
+ if ((cmd == SETACL && !get_handle ())
+ || (cmd != SETACL && !get_stat_handle ()))
{
query_open (cmd == SETACL ? query_write_control : query_read_control);
if (!(oret = open (O_BINARY, 0)))
if (!aclbufp)
set_errno(EFAULT);
else
- res = getacl (get_handle (), pc, nentries, aclbufp);
+ res = getacl (get_stat_handle (), pc, nentries, aclbufp);
+ /* For this ENOSYS case, see security.cc:get_file_attribute(). */
+ if (res == -1 && get_errno () == ENOSYS)
+ goto cant_access_acl;
break;
case GETACLCNT:
- res = getacl (get_handle (), pc, 0, NULL);
+ res = getacl (get_stat_handle (), pc, 0, NULL);
+ /* Ditto. */
+ if (res == -1 && get_errno () == ENOSYS)
+ goto cant_access_acl;
break;
default:
set_errno (EINVAL);
int
fhandler_disk_file::link (const char *newpath)
{
- extern bool allow_winsymlinks;
-
- path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX, stat_suffixes);
+ size_t nlen = strlen (newpath);
+ path_conv newpc (newpath, PC_SYM_NOFOLLOW | PC_POSIX | PC_NULLEMPTY, stat_suffixes);
if (newpc.error)
{
- set_errno (newpc.case_clash ? ECASECLASH : newpc.error);
+ set_errno (newpc.error);
return -1;
}
return -1;
}
- char new_buf[strlen (newpath) + 5];
- if (!newpc.error && !newpc.case_clash)
+ if (isdirsep (newpath[nlen - 1]) || has_dot_last_component (newpath, false))
{
- if (allow_winsymlinks && pc.is_lnk_special ())
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ char new_buf[nlen + 5];
+ if (!newpc.error)
+ {
+ /* If the original file is a lnk special file (except for sockets),
+ and if the original file has a .lnk suffix, add one to the hardlink
+ as well. */
+ if (pc.is_lnk_special () && !pc.issocket ()
+ && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
+ &ro_u_lnk, TRUE))
{
/* Shortcut hack. */
stpcpy (stpcpy (new_buf, newpath), ".lnk");
}
else if (!pc.isdir ()
&& pc.is_binary ()
+ && RtlEqualUnicodePathSuffix (pc.get_nt_native_path (),
+ &ro_u_exe, TRUE)
&& !RtlEqualUnicodePathSuffix (newpc.get_nt_native_path (),
- L".exe", TRUE))
+ &ro_u_exe, TRUE))
{
/* Executable hack. */
stpcpy (stpcpy (new_buf, newpath), ".exe");
}
}
- HANDLE fh;
- NTSTATUS status;
- OBJECT_ATTRIBUTES attr;
- IO_STATUS_BLOCK io;
- status = NtOpenFile (&fh, READ_CONTROL,
- pc.get_object_attr (attr, sec_none_nih), &io,
- FILE_SHARE_VALID_FLAGS,
- FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT);
- if (!NT_SUCCESS (status))
+ /* We only need READ_CONTROL access so the handle returned in pc is
+ sufficient. And if the file couldn't be opened with READ_CONTROL
+ access in path_conv, we won't be able to do it here anyway. */
+ HANDLE fh = get_stat_handle ();
+ if (!fh)
{
- __seterrno_from_nt_status (status);
+ set_errno (EACCES);
return -1;
}
PUNICODE_STRING tgt = newpc.get_nt_native_path ();
pfli->ReplaceIfExists = FALSE;
pfli->RootDirectory = NULL;
memcpy (pfli->FileName, tgt->Buffer, pfli->FileNameLength = tgt->Length);
+
+ NTSTATUS status;
+ IO_STATUS_BLOCK io;
status = NtSetInformationFile (fh, &io, pfli, size, FileLinkInformation);
- NtClose (fh);
if (!NT_SUCCESS (status))
{
if (status == STATUS_INVALID_DEVICE_REQUEST)
{
- /* FS doesn't support hard links. Try to copy file. */
- WCHAR pcw[pc.get_nt_native_path ()->Length + 1];
- WCHAR newpcw[newpc.get_nt_native_path ()->Length + 1];
- if (!CopyFileW (pc.get_wide_win32_path (pcw),
- newpc.get_wide_win32_path (newpcw), TRUE))
- {
- __seterrno ();
- return -1;
- }
- if (!allow_winsymlinks && pc.is_lnk_special ())
- SetFileAttributesW (newpcw, pc.file_attributes ()
- | FILE_ATTRIBUTE_SYSTEM
- | FILE_ATTRIBUTE_READONLY);
+ /* FS doesn't support hard links. Linux returns EPERM. */
+ set_errno (EPERM);
+ return -1;
}
else
{
int
fhandler_base::utimens_fs (const struct timespec *tvp)
{
- LARGE_INTEGER lastaccess, lastwrite;
struct timespec timeofday;
struct timespec tmp[2];
bool closeit = false;
closeit = true;
}
- gettimeofday (reinterpret_cast<struct timeval *> (&timeofday), 0);
- timeofday.tv_nsec *= 1000;
+ clock_gettime (CLOCK_REALTIME, &timeofday);
if (!tvp)
tmp[1] = tmp[0] = timeofday;
else
{
- if ((tmp[0].tv_nsec < UTIME_NOW || tmp[0].tv_nsec > 999999999L)
- || (tmp[1].tv_nsec < UTIME_NOW || tmp[1].tv_nsec > 999999999L))
+ if ((tvp[0].tv_nsec < UTIME_NOW || tvp[0].tv_nsec > 999999999L)
+ || (tvp[1].tv_nsec < UTIME_NOW || tvp[1].tv_nsec > 999999999L))
{
+ if (closeit)
+ close_fs ();
set_errno (EINVAL);
return -1;
}
tmp[0] = (tvp[0].tv_nsec == UTIME_NOW) ? timeofday : tvp[0];
tmp[1] = (tvp[1].tv_nsec == UTIME_NOW) ? timeofday : tvp[1];
}
- /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
- timespec_to_filetime (&tmp[0], (FILETIME *) &lastaccess);
- timespec_to_filetime (&tmp[1], (FILETIME *) &lastwrite);
debug_printf ("incoming lastaccess %08x %08x", tmp[0].tv_sec, tmp[0].tv_nsec);
IO_STATUS_BLOCK io;
FILE_BASIC_INFORMATION fbi;
+
fbi.CreationTime.QuadPart = 0LL;
- fbi.LastAccessTime = lastaccess;
- fbi.LastWriteTime = lastwrite;
+ /* UTIME_OMIT is handled in timespec_to_filetime by setting FILETIME to 0. */
+ timespec_to_filetime (&tmp[0], (LPFILETIME) &fbi.LastAccessTime);
+ timespec_to_filetime (&tmp[1], (LPFILETIME) &fbi.LastWriteTime);
fbi.ChangeTime.QuadPart = 0LL;
fbi.FileAttributes = 0;
NTSTATUS status = NtSetInformationFile (get_handle (), &io, &fbi, sizeof fbi,
FileBasicInformation);
+ /* For this special case for MVFS see the comment in
+ fhandler_disk_file::fchmod. */
+ if (pc.fs_is_mvfs () && NT_SUCCESS (status) && !closeit)
+ {
+ OBJECT_ATTRIBUTES attr;
+ HANDLE fh;
+
+ pc.init_reopen_attr (&attr, get_handle ());
+ if (NT_SUCCESS (NtOpenFile (&fh, FILE_WRITE_ATTRIBUTES, &attr, &io,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT)))
+ {
+ NtSetInformationFile (fh, &io, &fbi, sizeof fbi,
+ FileBasicInformation);
+ NtClose (fh);
+ }
+ }
if (closeit)
close_fs ();
/* Opening a directory on a 9x share from a NT machine works(!), but
int
fhandler_base::open_fs (int flags, mode_t mode)
{
- if (pc.case_clash && flags & O_CREAT)
- {
- debug_printf ("case clash detected");
- set_errno (ECASECLASH);
- return 0;
- }
-
/* Unfortunately NT allows to open directories for writing, but that's
disallowed according to SUSv3. */
if (pc.isdir () && (flags & O_ACCMODE) != O_RDONLY)
return 0;
}
- if (pc.hasgood_inode ())
- ino = get_ino_by_handle (get_handle ());
+ ino = pc.get_ino_by_handle (get_handle ());
/* A unique ID is necessary to recognize fhandler entries which are
duplicated by dup(2) or fork(2). */
AllocateLocallyUniqueId ((PLUID) &unique_id);
{
int res = -1;
SECURITY_ATTRIBUTES sa = sec_none_nih;
- security_descriptor sd;
-
- if (allow_ntsec && has_acls ())
- set_security_attribute (S_IFDIR | ((mode & 07777) & ~cygheap->umask),
- &sa, sd);
-
NTSTATUS status;
HANDLE dir;
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
- ULONG fattr = FILE_ATTRIBUTE_DIRECTORY;
+ PFILE_FULL_EA_INFORMATION p = NULL;
+ ULONG plen = 0;
+
+ if (pc.fs_is_nfs ())
+ {
+ /* When creating a dir on an NFS share, we have to set the
+ file mode by writing a NFS fattr3 structure with the
+ correct mode bits set. */
+ plen = sizeof (FILE_FULL_EA_INFORMATION) + sizeof (NFS_V3_ATTR)
+ + sizeof (fattr3);
+ p = (PFILE_FULL_EA_INFORMATION) alloca (plen);
+ p->NextEntryOffset = 0;
+ p->Flags = 0;
+ p->EaNameLength = sizeof (NFS_V3_ATTR) - 1;
+ p->EaValueLength = sizeof (fattr3);
+ strcpy (p->EaName, NFS_V3_ATTR);
+ fattr3 *nfs_attr = (fattr3 *) (p->EaName + p->EaNameLength + 1);
+ memset (nfs_attr, 0, sizeof (fattr3));
+ nfs_attr->type = NF3DIR;
+ nfs_attr->mode = (mode & 07777) & ~cygheap->umask;
+ }
status = NtCreateFile (&dir, FILE_LIST_DIRECTORY | SYNCHRONIZE,
pc.get_object_attr (attr, sa), &io, NULL,
- fattr, FILE_SHARE_VALID_FLAGS, FILE_CREATE,
+ FILE_ATTRIBUTE_DIRECTORY, FILE_SHARE_VALID_FLAGS,
+ FILE_CREATE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
| FILE_OPEN_FOR_BACKUP_INTENT,
- NULL, 0);
+ p, plen);
if (NT_SUCCESS (status))
{
+ if (has_acls ())
+ set_file_attribute (dir, pc, ILLEGAL_UID, ILLEGAL_GID,
+ S_JUSTCREATED | S_IFDIR
+ | ((mode & 07777) & ~cygheap->umask));
NtClose (dir);
res = 0;
}
/* Check for existence of remote dirs after trying to delete them.
Two reasons:
- Sometimes SMB indicates failure when it really succeeds.
- - Removeing a directory on a samba drive doesn't return an error if the
- directory can't be removed because it's not empty. */
+ - Removing a directory on a Samba drive using an old Samba version
+ sometimes doesn't return an error, if the directory can't be removed
+ because it's not empty. */
if (isremote ())
{
OBJECT_ATTRIBUTES attr;
FILE_BASIC_INFORMATION fbi;
+ NTSTATUS q_status;
- if (NT_SUCCESS (NtQueryAttributesFile
- (pc.get_object_attr (attr, sec_none_nih), &fbi)))
- status = STATUS_DIRECTORY_NOT_EMPTY;
- else
+ q_status = NtQueryAttributesFile (pc.get_object_attr (attr, sec_none_nih), &fbi);
+ if (!NT_SUCCESS (status) && q_status == STATUS_OBJECT_NAME_NOT_FOUND)
status = STATUS_SUCCESS;
+ else if (NT_SUCCESS (status) && NT_SUCCESS (q_status))
+ status = STATUS_DIRECTORY_NOT_EMPTY;
}
if (!NT_SUCCESS (status))
{
OBJECT_ATTRIBUTES attr;
NTSTATUS status;
IO_STATUS_BLOCK io;
-
- status = NtOpenFile (&get_handle (),
- SYNCHRONIZE | FILE_LIST_DIRECTORY,
- pc.get_object_attr (attr, sec_none_nih),
- &io, FILE_SHARE_VALID_FLAGS,
- FILE_SYNCHRONOUS_IO_NONALERT
- | FILE_OPEN_FOR_BACKUP_INTENT
- | FILE_DIRECTORY_FILE);
- if (!NT_SUCCESS (status))
+ /* Tools like ls(1) call dirfd() to fetch the directory
+ descriptor for calls to facl or fstat. The tight access mask
+ used so far is not sufficient to reuse the handle for these
+ calls, instead the facl/fstat calls find the handle to be
+ unusable and have to re-open the file for reading attributes
+ and control data. So, what we do here is to try to open the
+ directory with more relaxed access mask which enables to use
+ the handle for the aforementioned purpose. This should work
+ in almost all cases. Only if it doesn't work due to
+ permission problems, we drop the additional access bits and
+ try again. */
+ ACCESS_MASK fstat_mask = READ_CONTROL | FILE_READ_ATTRIBUTES;
+
+ do
{
- __seterrno_from_nt_status (status);
- goto free_mounts;
+ status = NtOpenFile (&get_handle (),
+ SYNCHRONIZE | FILE_LIST_DIRECTORY
+ | fstat_mask,
+ pc.get_object_attr (attr, sec_none_nih),
+ &io, FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ | FILE_OPEN_FOR_BACKUP_INTENT
+ | FILE_DIRECTORY_FILE);
+ if (!NT_SUCCESS (status))
+ {
+ if (status == STATUS_ACCESS_DENIED && fstat_mask)
+ fstat_mask = 0;
+ else
+ {
+ __seterrno_from_nt_status (status);
+ goto free_mounts;
+ }
+ }
}
+ while (!NT_SUCCESS (status));
}
/* FileIdBothDirectoryInformation is apparently unsupported on
OS/FS combinations (say, Win2K/CDFS or so). Instead of
testing in readdir for yet another error code, let's use
FileIdBothDirectoryInformation only on filesystems supporting
- persistent ACLs, FileBothDirectoryInformation otherwise. */
+ persistent ACLs, FileBothDirectoryInformation otherwise.
+
+ NFS clients hide dangling symlinks from directory queries,
+ unless you use the FileNamesInformation info class.
+ On newer NFS clients (>=Vista) FileIdBothDirectoryInformation
+ works fine, but only if the NFS share is mounted to a drive
+ letter. TODO: We don't test that here for now, but it might
+ be worth to test if there's a speed gain in using
+ FileIdBothDirectoryInformation, because it doesn't require to
+ open the file to read the inode number. */
if (pc.hasgood_inode ())
{
dir->__flags |= dirent_set_d_ino;
- if (wincap.has_fileid_dirinfo ())
+ if (pc.fs_is_nfs ())
+ dir->__flags |= dirent_nfs_d_ino;
+ else if (wincap.has_fileid_dirinfo ()
+ && !pc.has_buggy_fileid_dirinfo ())
dir->__flags |= dirent_get_d_ino;
}
}
strcpy (c, "..");
path = fname;
}
- path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX);
+ path_conv pc (path, PC_SYM_NOFOLLOW | PC_POSIX | PC_NOWARN | PC_KEEP_HANDLE);
if (pc.isspecial ())
{
if (!stat_worker (pc, &st))
}
else if (!pc.hasgood_inode ())
ino = hash_path_name (0, pc.get_nt_native_path ());
- else if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
- pc.get_object_attr (attr, sec_none_nih),
- &io, FILE_SHARE_VALID_FLAGS,
- FILE_OPEN_FOR_BACKUP_INTENT
- | (pc.is_rep_symlink ()
- ? FILE_OPEN_REPARSE_POINT : 0))))
- {
- ino = get_ino_by_handle (hdl);
- NtClose (hdl);
+ else if ((hdl = pc.handle ()) != NULL
+ || NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL,
+ pc.get_object_attr (attr, sec_none_nih),
+ &io, FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT
+ | (pc.is_rep_symlink ()
+ ? FILE_OPEN_REPARSE_POINT : 0)))
+ )
+ {
+ ino = pc.get_ino_by_handle (hdl);
+ if (!ino)
+ ino = hash_path_name (0, pc.get_nt_native_path ());
}
return ino;
}
if ((de->d_ino = d_mounts (dir)->check_missing_mount (fname)))
added = true;
if (!added)
- return geterrno_from_win_error (w32_err);
+ {
+ fname->Length = 0;
+ return geterrno_from_win_error (w32_err);
+ }
attr = 0;
dir->__flags &= ~dirent_set_d_ino;
}
+ /* Set d_type if type can be determined from file attributes. For .lnk
+ symlinks, d_type will be reset below. Reparse points can be NTFS
+ symlinks, even if they have the FILE_ATTRIBUTE_DIRECTORY flag set. */
+ if (attr &&
+ !(attr & (~FILE_ATTRIBUTE_VALID_FLAGS | FILE_ATTRIBUTE_REPARSE_POINT)))
+ {
+ if (attr & FILE_ATTRIBUTE_DIRECTORY)
+ de->d_type = DT_DIR;
+ /* FILE_ATTRIBUTE_SYSTEM might denote system-bit type symlinks. */
+ else if (!(attr & FILE_ATTRIBUTE_SYSTEM))
+ de->d_type = DT_REG;
+ }
+
/* Check for directory reparse point. These are potential volume mount
points which have another inode than the underlying directory. */
if ((attr & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_REPARSE_POINT))
OBJECT_ATTRIBUTES attr;
IO_STATUS_BLOCK io;
- InitializeObjectAttributes (&attr, fname, OBJ_CASE_INSENSITIVE,
+ InitializeObjectAttributes (&attr, fname, pc.objcaseinsensitive (),
get_handle (), NULL);
- if (is_volume_mountpoint (&attr)
- && (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
- FILE_SHARE_VALID_FLAGS,
- FILE_OPEN_FOR_BACKUP_INTENT))))
+ de->d_type = readdir_check_reparse_point (&attr);
+ if (de->d_type == DT_DIR)
{
- de->d_ino = get_ino_by_handle (reph);
- NtClose (reph);
+ /* Volume mountpoints are treated as directories. We have to fix
+ the inode number, otherwise we have the inode number of the
+ mount point, rather than the inode number of the toplevel
+ directory of the mounted drive. */
+ if (NT_SUCCESS (NtOpenFile (&reph, READ_CONTROL, &attr, &io,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT)))
+ {
+ de->d_ino = pc.get_ino_by_handle (reph);
+ NtClose (reph);
+ }
}
}
- /* Check for Windows shortcut. If it's a Cygwin or U/WIN
- symlink, drop the .lnk suffix. */
- if ((attr & FILE_ATTRIBUTE_READONLY) && fname->Length > 4 * sizeof (WCHAR))
+ /* Check for Windows shortcut. If it's a Cygwin or U/WIN symlink, drop the
+ .lnk suffix and set d_type accordingly. */
+ if ((attr & (FILE_ATTRIBUTE_DIRECTORY
+ | FILE_ATTRIBUTE_REPARSE_POINT
+ | FILE_ATTRIBUTE_READONLY)) == FILE_ATTRIBUTE_READONLY
+ && fname->Length > 4 * sizeof (WCHAR))
{
UNICODE_STRING uname;
- UNICODE_STRING lname;
RtlInitCountedUnicodeString (&uname,
fname->Buffer
+ fname->Length / sizeof (WCHAR) - 4,
4 * sizeof (WCHAR));
- RtlInitUnicodeString (&lname, (PWCHAR) L".lnk");
- if (RtlEqualUnicodeString (&uname, &lname, TRUE))
+ if (RtlEqualUnicodeString (&uname, &ro_u_lnk, TRUE))
{
tmp_pathbuf tp;
UNICODE_STRING fbuf;
-
+
tp.u_get (&fbuf);
RtlCopyUnicodeString (&fbuf, pc.get_nt_native_path ());
RtlAppendUnicodeToString (&fbuf, L"\\");
fbuf.Length -= 2 * sizeof (WCHAR);
}
path_conv fpath (&fbuf, PC_SYM_NOFOLLOW);
- if (fpath.issymlink () || fpath.is_fs_special ())
- fname->Length -= 4 * sizeof (WCHAR);
+ if (fpath.issymlink ())
+ {
+ fname->Length -= 4 * sizeof (WCHAR);
+ de->d_type = DT_LNK;
+ }
+ else if (fpath.isfifo ())
+ {
+ fname->Length -= 4 * sizeof (WCHAR);
+ de->d_type = DT_FIFO;
+ }
+ else if (fpath.is_fs_special ())
+ {
+ fname->Length -= 4 * sizeof (WCHAR);
+ de->d_type = S_ISCHR (fpath.dev.mode) ? DT_CHR : DT_BLK;
+ }
}
}
- /* Transform special DOS chars back to normal. */
- for (USHORT i = 0; i < fname->Length / sizeof (WCHAR); ++i)
- if ((fname->Buffer[i] & 0xff00) == 0xf000)
- fname->Buffer[i] &= 0xff;
-#if 0
- if (pc.isencoded ())
+ sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
+ fname->Length / sizeof (WCHAR));
+
+ /* Don't try to optimize relative to dir->__d_position. On several
+ filesystems it's no safe bet that "." and ".." entries always
+ come first. */
+ if (de->d_name[0] == '.')
{
- char tmp[NAME_MAX + 1];
- sys_wcstombs (tmp, NAME_MAX + 1, fname->Buffer,
- fname->Length / sizeof (WCHAR));
- fnunmunge (de->d_name, tmp);
+ if (de->d_name[1] == '\0')
+ dir->__flags |= dirent_saw_dot;
+ else if (de->d_name[1] == '.' && de->d_name[2] == '\0')
+ dir->__flags |= dirent_saw_dot_dot;
}
- else
-#endif
- sys_wcstombs (de->d_name, NAME_MAX + 1, fname->Buffer,
- fname->Length / sizeof (WCHAR));
-
- if (dir->__d_position == 0 && !strcmp (de->d_name, "."))
- dir->__flags |= dirent_saw_dot;
- else if (dir->__d_position == 1 && !strcmp (de->d_name, ".."))
- dir->__flags |= dirent_saw_dot_dot;
return 0;
}
NTSTATUS status = STATUS_SUCCESS;
PFILE_ID_BOTH_DIR_INFORMATION buf = NULL;
PWCHAR FileName;
+ ULONG FileNameLength;
+ ULONG FileAttributes = 0;
IO_STATUS_BLOCK io;
UNICODE_STRING fname;
{
if ((dir->__flags & dirent_get_d_ino))
{
- status = NtQueryDirectoryFile (get_handle (), NULL, NULL, 0, &io,
+ status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
d_cache (dir), DIR_BUF_SIZE,
FileIdBothDirectoryInformation,
FALSE, NULL, dir->__d_position == 0);
/* FileIdBothDirectoryInformation isn't supported for remote drives
on NT4 and 2K systems, and it's also not supported on 2K at all,
- when accessing network drives on any remote OS. We just fall
- back to using a standard directory query in this case and note
- this case using the dirent_get_d_ino flag. */
- if (status == STATUS_INVALID_LEVEL
- || status == STATUS_INVALID_PARAMETER
- || status == STATUS_INVALID_INFO_CLASS)
+ when accessing network drives on any remote OS. There are also
+ hacked versions of Samba 3.0.x out there (Debian-based it seems),
+ which return STATUS_NOT_SUPPORTED rather than handling this info
+ class. We just fall back to using a standard directory query in
+ this case and note this case using the dirent_get_d_ino flag. */
+ if (!NT_SUCCESS (status) && status != STATUS_NO_MORE_FILES
+ && (status == STATUS_INVALID_LEVEL
+ || status == STATUS_NOT_SUPPORTED
+ || status == STATUS_INVALID_PARAMETER
+ || status == STATUS_INVALID_NETWORK_RESPONSE
+ || status == STATUS_INVALID_INFO_CLASS))
dir->__flags &= ~dirent_get_d_ino;
/* Something weird happens on Samba up to version 3.0.21c, which is
fixed in 3.0.22. FileIdBothDirectoryInformation seems to work
if (d_cachepos (dir) == 0)
{
status = NtQueryDirectoryFile (get_handle (), NULL, NULL,
- 0, &io, d_cache (dir), DIR_BUF_SIZE,
+ NULL, &io, d_cache (dir),
+ DIR_BUF_SIZE,
FileBothDirectoryInformation,
FALSE, NULL, cnt == 0);
if (!NT_SUCCESS (status))
}
}
if (!(dir->__flags & dirent_get_d_ino))
- status = NtQueryDirectoryFile (get_handle (), NULL, NULL, 0, &io,
+ status = NtQueryDirectoryFile (get_handle (), NULL, NULL, NULL, &io,
d_cache (dir), DIR_BUF_SIZE,
- FileBothDirectoryInformation,
+ (dir->__flags & dirent_nfs_d_ino)
+ ? FileNamesInformation
+ : FileBothDirectoryInformation,
FALSE, NULL, dir->__d_position == 0);
}
go_ahead:
- if (!NT_SUCCESS (status))
+ if (status == STATUS_NO_MORE_FILES)
+ /*nothing*/;
+ else if (!NT_SUCCESS (status))
debug_printf ("NtQueryDirectoryFile failed, status %p, win32 error %lu",
status, RtlNtStatusToDosError (status));
else
if ((dir->__flags & dirent_get_d_ino))
{
FileName = buf->FileName;
+ FileNameLength = buf->FileNameLength;
+ FileAttributes = buf->FileAttributes;
if ((dir->__flags & dirent_set_d_ino))
de->d_ino = buf->FileId.QuadPart;
}
+ else if ((dir->__flags & dirent_nfs_d_ino))
+ {
+ FileName = ((PFILE_NAMES_INFORMATION) buf)->FileName;
+ FileNameLength = ((PFILE_NAMES_INFORMATION) buf)->FileNameLength;
+ }
else
- FileName = ((PFILE_BOTH_DIR_INFORMATION) buf)->FileName;
- RtlInitCountedUnicodeString (&fname, FileName, buf->FileNameLength);
+ {
+ FileName = ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileName;
+ FileNameLength =
+ ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileNameLength;
+ FileAttributes =
+ ((PFILE_BOTH_DIRECTORY_INFORMATION) buf)->FileAttributes;
+ }
+ RtlInitCountedUnicodeString (&fname, FileName, FileNameLength);
de->d_ino = d_mounts (dir)->check_mount (&fname, de->d_ino);
if (de->d_ino == 0 && (dir->__flags & dirent_set_d_ino))
{
- OBJECT_ATTRIBUTES attr;
-
- if (dir->__d_position == 0 && buf->FileNameLength == 2
- && FileName[0] == '.')
- de->d_ino = get_ino_by_handle (get_handle ());
- else if (dir->__d_position == 1 && buf->FileNameLength == 4
+ /* Don't try to optimize relative to dir->__d_position. On several
+ filesystems it's no safe bet that "." and ".." entries always
+ come first. */
+ if (FileNameLength == sizeof (WCHAR) && FileName[0] == '.')
+ de->d_ino = pc.get_ino_by_handle (get_handle ());
+ else if (FileNameLength == 2 * sizeof (WCHAR)
&& FileName[0] == L'.' && FileName[1] == L'.')
- if (!(dir->__flags & dirent_isroot))
- de->d_ino = readdir_get_ino (get_name (), true);
- else
- de->d_ino = get_ino_by_handle (get_handle ());
+ {
+ if (!(dir->__flags & dirent_isroot))
+ de->d_ino = readdir_get_ino (get_name (), true);
+ else
+ de->d_ino = pc.get_ino_by_handle (get_handle ());
+ }
else
{
+ OBJECT_ATTRIBUTES attr;
HANDLE hdl;
+ NTSTATUS f_status;
- InitializeObjectAttributes (&attr, &fname, OBJ_CASE_INSENSITIVE,
+ InitializeObjectAttributes (&attr, &fname,
+ pc.objcaseinsensitive (),
get_handle (), NULL);
- if (NT_SUCCESS (NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
- FILE_SHARE_VALID_FLAGS,
- FILE_OPEN_FOR_BACKUP_INTENT)))
+ /* FILE_OPEN_REPARSE_POINT on NFS is a no-op, so the normal
+ NtOpenFile here returns the inode number of the symlink target,
+ rather than the inode number of the symlink itself.
+
+ Worse, trying to open a symlink without setting the special
+ "ActOnSymlink" EA triggers a bug in Windows 7 which results
+ in a timeout of up to 20 seconds, followed by two exceptions
+ in the NT kernel.
+
+ Since both results are far from desirable, we open symlinks
+ on NFS so that we get the right inode and a happy W7.
+ And, since some filesystems choke on the EAs, we don't
+ use them unconditionally. */
+ f_status = (dir->__flags & dirent_nfs_d_ino)
+ ? NtCreateFile (&hdl, READ_CONTROL, &attr, &io,
+ NULL, 0, FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN, FILE_OPEN_FOR_BACKUP_INTENT,
+ &nfs_aol_ffei, sizeof nfs_aol_ffei)
+ : NtOpenFile (&hdl, READ_CONTROL, &attr, &io,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT
+ | FILE_OPEN_REPARSE_POINT);
+ if (NT_SUCCESS (f_status))
{
- de->d_ino = get_ino_by_handle (hdl);
+ /* We call NtQueryInformationFile here, rather than
+ pc.get_ino_by_handle(), otherwise we can't short-circuit
+ dirent_set_d_ino correctly. */
+ FILE_INTERNAL_INFORMATION fai;
+ f_status = NtQueryInformationFile (hdl, &io, &fai, sizeof fai,
+ FileInternalInformation);
NtClose (hdl);
+ if (NT_SUCCESS (f_status))
+ {
+ if (pc.isgood_inode (fai.FileId.QuadPart))
+ de->d_ino = fai.FileId.QuadPart;
+ else
+ /* Untrusted file system. Don't try to fetch inode
+ number again. */
+ dir->__flags &= ~dirent_set_d_ino;
+ }
}
}
- /* Enforce namehash as inode number on untrusted file systems. */
- if (!pc.isgood_inode (de->d_ino))
- {
- dir->__flags &= ~dirent_set_d_ino;
- de->d_ino = 0;
- }
}
}
if (!(res = readdir_helper (dir, de, RtlNtStatusToDosError (status),
- buf ? buf->FileAttributes : 0, &fname)))
+ FileAttributes, &fname)))
dir->__d_position++;
else if (!(dir->__flags & dirent_saw_dot))
{
strcpy (de->d_name , ".");
- de->d_ino = get_ino_by_handle (get_handle ());
+ de->d_ino = pc.get_ino_by_handle (get_handle ());
+ de->d_type = DT_DIR;
dir->__d_position++;
dir->__flags |= dirent_saw_dot;
res = 0;
if (!(dir->__flags & dirent_isroot))
de->d_ino = readdir_get_ino (get_name (), true);
else
- de->d_ino = get_ino_by_handle (get_handle ());
+ de->d_ino = pc.get_ino_by_handle (get_handle ());
+ de->d_type = DT_DIR;
dir->__d_position++;
dir->__flags |= dirent_saw_dot_dot;
res = 0;
}
- syscall_printf ("%d = readdir (%p, %p) (%s)", res, dir, &de, res ? "***" : de->d_name);
+ syscall_printf ("%d = readdir (%p, %p) (L\"%lS\" > \"%ls\") (attr %p > type %d)",
+ res, dir, &de, res ? NULL : &fname, res ? "***" : de->d_name,
+ FileAttributes, de->d_type);
return res;
}
-_off64_t
+long
fhandler_disk_file::telldir (DIR *dir)
{
return dir->__d_position;
}
void
-fhandler_disk_file::seekdir (DIR *dir, _off64_t loc)
+fhandler_disk_file::seekdir (DIR *dir, long loc)
{
rewinddir (dir);
while (loc > dir->__d_position)
to NtQueryDirectoryFile on remote shares is ignored, thus
resulting in not being able to rewind on remote shares. By
reopening the directory, we get a fresh new directory pointer. */
- UNICODE_STRING fname;
OBJECT_ATTRIBUTES attr;
NTSTATUS status;
IO_STATUS_BLOCK io;
HANDLE new_dir;
- RtlInitUnicodeString (&fname, L"");
- InitializeObjectAttributes (&attr, &fname, OBJ_CASE_INSENSITIVE,
- get_handle (), NULL);
+ pc.init_reopen_attr (&attr, get_handle ());
status = NtOpenFile (&new_dir, SYNCHRONIZE | FILE_LIST_DIRECTORY,
&attr, &io, FILE_SHARE_VALID_FLAGS,
FILE_SYNCHRONOUS_IO_NONALERT
return 0;
}
-#define DRVSZ sizeof ("x:\\")
void
fhandler_cygdrive::set_drives ()
{
- const int len = 2 + 26 * DRVSZ;
- char *p = const_cast<char *> (get_win32_name ());
- pdrive = p;
- ndrives = GetLogicalDriveStrings (len, p) / DRVSZ;
+ pdrive = pdrive_buf;
+ ndrives = GetLogicalDriveStrings (sizeof pdrive_buf, pdrive_buf) / DRVSZ;
}
int
{
fhandler_base::fstat (buf);
buf->st_ino = 2;
- buf->st_mode = S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ buf->st_mode = S_IFDIR | STD_RBITS | STD_XBITS;
if (!ndrives)
set_drives ();
char flptst[] = "X:";
for (const char *p = pdrive; p && *p; p = strchr (p, '\0') + 1)
if (is_floppy ((flptst[0] = *p, flptst))
|| GetFileAttributes (p) == INVALID_FILE_ATTRIBUTES)
- --n;
+ n--;
buf->st_nlink = n + 2;
return 0;
}
void
fhandler_cygdrive::rewinddir (DIR *dir)
{
- pdrive = get_win32_name ();
+ pdrive = pdrive_buf;
dir->__d_position = 0;
}
int
fhandler_cygdrive::closedir (DIR *dir)
{
- pdrive = get_win32_name ();
+ pdrive = pdrive_buf;
return 0;
}