summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/fhandler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/fhandler.cc')
-rw-r--r--winsup/cygwin/fhandler.cc161
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++)