diff options
Diffstat (limited to 'winsup/cygwin/fhandler.cc')
-rw-r--r-- | winsup/cygwin/fhandler.cc | 161 |
1 files changed, 158 insertions, 3 deletions
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc index 0bad75cc9..6e2db18df 100644 --- a/winsup/cygwin/fhandler.cc +++ b/winsup/cygwin/fhandler.cc @@ -24,6 +24,8 @@ details. */ #include "dtable.h" #include "cygheap.h" #include "shared_info.h" +#include "sigproc.h" +#include "pinfo.h" #include <assert.h> static NO_COPY const int CHUNK_SIZE = 1024; /* Used for crlf conversions */ @@ -847,15 +849,168 @@ rootdir(char *full_path) return root; } +int __stdcall +fhandler_base::fstat (struct stat *buf, path_conv *) +{ + switch (get_device ()) + { + case FH_PIPEW: + buf->st_mode = STD_WBITS | S_IWGRP | S_IWOTH; + break; + case FH_PIPER: + buf->st_mode = STD_RBITS; + break; + default: + buf->st_mode = STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH; + break; + } + + buf->st_mode |= get_device () == FH_FLOPPY ? S_IFBLK : S_IFCHR; + buf->st_nlink = 1; + buf->st_blksize = S_BLKSIZE; + buf->st_dev = buf->st_rdev = FHDEVN (get_device ()) << 8 | (get_unit () & 0xff); + buf->st_ino = get_namehash (); + buf->st_atime = buf->st_mtime = buf->st_ctime = time (NULL); + return 0; +} + +static int +num_entries (const char *win32_name) +{ + WIN32_FIND_DATA buf; + HANDLE handle; + char buf1[MAX_PATH]; + int count = 0; + + strcpy (buf1, win32_name); + int len = strlen (buf1); + if (len == 0 || isdirsep (buf1[len - 1])) + strcat (buf1, "*"); + else + strcat (buf1, "/*"); /* */ + + handle = FindFirstFileA (buf1, &buf); + + if (handle == INVALID_HANDLE_VALUE) + return 0; + count ++; + while (FindNextFileA (handle, &buf)) + { + if ((buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + count ++; + } + FindClose (handle); + return count; +} + int -fhandler_disk_file::fstat (struct stat *buf) +fhandler_disk_file::fstat (struct stat *buf, path_conv *pc) +{ + int res = -1; + int oret; + uid_t uid; + gid_t gid; + int open_flags = O_RDONLY | O_BINARY | O_DIROPEN; + + if (!pc) + return fstat_helper (buf); + + if ((oret = open (pc, open_flags, 0))) + /* ok */; + else + { + int ntsec_atts = 0; + /* If we couldn't open the file, try a "query open" with no permissions. + This will allow us to determine *some* things about the file, at least. */ + set_query_open (TRUE); + if ((oret = open (pc, open_flags, 0))) + /* ok */; + else if (allow_ntsec && pc->has_acls () && get_errno () == EACCES + && !get_file_attribute (TRUE, get_win32_name (), &ntsec_atts, &uid, &gid) + && !ntsec_atts && uid == myself->uid && gid == myself->gid) + { + /* Check a special case here. If ntsec is ON it happens + that a process creates a file using mode 000 to disallow + other processes access. In contrast to UNIX, this results + in a failing open call in the same process. Check that + case. */ + set_file_attribute (TRUE, get_win32_name (), 0400); + oret = open (pc, open_flags, 0); + set_file_attribute (TRUE, get_win32_name (), ntsec_atts); + } + } + if (oret) + { + res = fstat_helper (buf); + /* 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 and + set the number of links to 2. */ + /* Unfortunately the count of 2 confuses `find (1)' command. So + let's try it with `1' as link count. */ + if (pc->isdir ()) + buf->st_nlink = (pc->isremote () + ? 1 : num_entries (pc->get_win32 ())); + close (); + } + else if (pc->exists ()) + { + /* Unfortunately, the above open may fail if the file exists, though. + So we have to care for this case here, too. */ + WIN32_FIND_DATA wfd; + HANDLE handle; + buf->st_nlink = 1; + if (pc->isdir () && pc->isremote ()) + buf->st_nlink = num_entries (pc->get_win32 ()); + buf->st_dev = FHDEVN (FH_DISK) << 8; + buf->st_ino = hash_path_name (0, pc->get_win32 ()); + if (pc->isdir ()) + buf->st_mode = S_IFDIR; + else if (pc->issymlink ()) + buf->st_mode = S_IFLNK; + else if (pc->issocket ()) + buf->st_mode = S_IFSOCK; + else + buf->st_mode = S_IFREG; + if (!pc->has_acls () + || get_file_attribute (TRUE, pc->get_win32 (), + &buf->st_mode, + &buf->st_uid, &buf->st_gid)) + { + buf->st_mode |= STD_RBITS | STD_XBITS; + if (!(pc->has_attribute (FILE_ATTRIBUTE_READONLY))) + buf->st_mode |= STD_WBITS; + if (pc->issymlink ()) + buf->st_mode |= S_IRWXU | S_IRWXG | S_IRWXO; + get_file_attribute (FALSE, pc->get_win32 (), + NULL, &buf->st_uid, &buf->st_gid); + } + if ((handle = FindFirstFile (pc->get_win32 (), &wfd)) + != INVALID_HANDLE_VALUE) + { + buf->st_atime = to_time_t (&wfd.ftLastAccessTime); + buf->st_mtime = to_time_t (&wfd.ftLastWriteTime); + buf->st_ctime = to_time_t (&wfd.ftCreationTime); + buf->st_size = wfd.nFileSizeLow; + buf->st_blksize = S_BLKSIZE; + buf->st_blocks = ((unsigned long) buf->st_size + + S_BLKSIZE-1) / S_BLKSIZE; + FindClose (handle); + } + res = 0; + } + + return res; +} + +int +fhandler_disk_file::fstat_helper (struct stat *buf) { int res = 0; // avoid a compiler warning BY_HANDLE_FILE_INFORMATION local; save_errno saved_errno; - memset (buf, 0, sizeof (*buf)); - /* NT 3.51 seems to have a bug when attempting to get vol serial numbers. This loop gets around this. */ for (int i = 0; i < 2; i++) |