Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/fhandler/procsys.cc')
-rw-r--r--winsup/cygwin/fhandler/procsys.cc486
1 files changed, 486 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler/procsys.cc b/winsup/cygwin/fhandler/procsys.cc
new file mode 100644
index 000000000..cd1d35984
--- /dev/null
+++ b/winsup/cygwin/fhandler/procsys.cc
@@ -0,0 +1,486 @@
+/* fhandler_procsys.cc: fhandler for native NT namespace.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdlib.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include <winioctl.h>
+#include "ntdll.h"
+#include "tls_pbuf.h"
+
+#include <dirent.h>
+
+/* Path of the /proc/sys filesystem */
+const char procsys[] = "/proc/sys";
+const size_t procsys_len = sizeof (procsys) - 1;
+
+#define mk_unicode_path(p) \
+ WCHAR namebuf[strlen (get_name ()) + 1]; \
+ { \
+ const char *from; \
+ PWCHAR to; \
+ for (to = namebuf, from = get_name () + procsys_len; *from; \
+ to++, from++) \
+ /* The NT device namespace is ASCII only. */ \
+ *to = (*from == '/') ? L'\\' : (WCHAR) *from; \
+ if (to == namebuf) \
+ *to++ = L'\\'; \
+ *to = L'\0'; \
+ RtlInitUnicodeString ((p), namebuf); \
+ }
+
+virtual_ftype_t
+fhandler_procsys::exists (struct stat *buf)
+{
+ UNICODE_STRING path;
+ UNICODE_STRING dir;
+ OBJECT_ATTRIBUTES attr;
+ IO_STATUS_BLOCK io;
+ NTSTATUS status;
+ HANDLE h;
+ FILE_BASIC_INFORMATION fbi;
+ bool internal = false;
+ bool desperate_parent_check = false;
+ /* Default device type is character device. */
+ virtual_ftype_t file_type = virt_chr;
+
+ if (strlen (get_name ()) == procsys_len)
+ return virt_rootdir;
+ mk_unicode_path (&path);
+
+ /* Try to open parent dir. If it works, the object is definitely
+ an object within the internal namespace. We don't need to test
+ it for being a file or dir on the filesystem anymore. If the
+ error is STATUS_OBJECT_TYPE_MISMATCH, we know that the file
+ itself is external. Otherwise we don't know. */
+ RtlSplitUnicodePath (&path, &dir, NULL);
+ /* RtlSplitUnicodePath preserves the trailing backslash in dir. Don't
+ preserve it to open the dir, unless it's the object root. */
+ if (dir.Length > sizeof (WCHAR))
+ dir.Length -= sizeof (WCHAR);
+ InitializeObjectAttributes (&attr, &dir, OBJ_CASE_INSENSITIVE, NULL, NULL);
+ status = NtOpenDirectoryObject (&h, DIRECTORY_QUERY, &attr);
+ debug_printf ("NtOpenDirectoryObject: %y", status);
+ if (NT_SUCCESS (status))
+ {
+ internal = true;
+ NtClose (h);
+ }
+
+ /* First check if the object is a symlink. */
+ InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL);
+ status = NtOpenSymbolicLinkObject (&h, READ_CONTROL | SYMBOLIC_LINK_QUERY,
+ &attr);
+ debug_printf ("NtOpenSymbolicLinkObject: %y", status);
+ if (NT_SUCCESS (status))
+ {
+ /* If requested, check permissions. */
+ if (buf)
+ get_object_attribute (h, &buf->st_uid, &buf->st_gid, &buf->st_mode);
+ NtClose (h);
+ return virt_symlink;
+ }
+ else if (status == STATUS_ACCESS_DENIED)
+ return virt_symlink;
+ /* Then check if it's an object directory. */
+ status = NtOpenDirectoryObject (&h, READ_CONTROL | DIRECTORY_QUERY, &attr);
+ debug_printf ("NtOpenDirectoryObject: %y", status);
+ if (NT_SUCCESS (status))
+ {
+ /* If requested, check permissions. */
+ if (buf)
+ get_object_attribute (h, &buf->st_uid, &buf->st_gid, &buf->st_mode);
+ NtClose (h);
+ return virt_directory;
+ }
+ else if (status == STATUS_ACCESS_DENIED)
+ return virt_directory;
+ /* Next try to open as file/device. */
+ status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES, &attr, &io,
+ FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT);
+ debug_printf ("NtOpenFile: %y", status);
+ /* Name is invalid, that's nothing. */
+ if (status == STATUS_OBJECT_NAME_INVALID)
+ return virt_none;
+ /* If no media is found, or we get this dreaded sharing violation, let
+ the caller immediately try again as normal file. */
+ if (status == STATUS_NO_MEDIA_IN_DEVICE
+ || status == STATUS_SHARING_VIOLATION)
+ return virt_fsfile; /* Just try again as normal file. */
+ /* If file or path can't be found, let caller try again as normal file. */
+ if (status == STATUS_OBJECT_PATH_NOT_FOUND
+ || status == STATUS_OBJECT_NAME_NOT_FOUND)
+ return virt_fsfile;
+ /* Check for pipe errors, which make a good hint... */
+ if (status >= STATUS_PIPE_NOT_AVAILABLE && status <= STATUS_PIPE_BUSY)
+ return virt_pipe;
+ if (status == STATUS_ACCESS_DENIED && !internal)
+ {
+ /* Check if this is just some file or dir on a real FS to circumvent
+ most permission problems. Don't try that on internal objects,
+ since NtQueryAttributesFile might crash the machine if the underlying
+ driver is badly written. */
+ status = NtQueryAttributesFile (&attr, &fbi);
+ debug_printf ("NtQueryAttributesFile: %y", status);
+ if (NT_SUCCESS (status))
+ return (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ ? virt_fsdir : virt_fsfile;
+ /* Ok, so we're desperate and the file still maybe on some filesystem.
+ To check this, we now split the path until we can finally access any
+ of the parent's. Then we fall through to check the parent type. In
+ contrast to the first parent check, we now check explicitely with
+ trailing backslash. This will fail for directories in the internal
+ namespace, so we won't accidentally test those. */
+ dir = path;
+ InitializeObjectAttributes (&attr, &dir, OBJ_CASE_INSENSITIVE,
+ NULL, NULL);
+ do
+ {
+ RtlSplitUnicodePath (&dir, &dir, NULL);
+ status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES,
+ &attr, &io, FILE_SHARE_VALID_FLAGS,
+ FILE_OPEN_FOR_BACKUP_INTENT);
+ debug_printf ("NtOpenDirectoryObject: %y", status);
+ if (dir.Length > sizeof (WCHAR))
+ dir.Length -= sizeof (WCHAR);
+ }
+ while (dir.Length > sizeof (WCHAR) && !NT_SUCCESS (status));
+ desperate_parent_check = true;
+ }
+ if (NT_SUCCESS (status))
+ {
+ FILE_FS_DEVICE_INFORMATION ffdi;
+
+ /* If requested, check permissions. If this is a parent handle from
+ the above desperate parent check, skip. */
+ if (buf && !desperate_parent_check)
+ get_object_attribute (h, &buf->st_uid, &buf->st_gid, &buf->st_mode);
+
+ /* Check for the device type. */
+ status = NtQueryVolumeInformationFile (h, &io, &ffdi, sizeof ffdi,
+ FileFsDeviceInformation);
+ debug_printf ("NtQueryVolumeInformationFile: %y", status);
+ /* Don't call NtQueryInformationFile unless we know it's a safe type.
+ The call is known to crash machines, if the underlying driver is
+ badly written. */
+ if (NT_SUCCESS (status))
+ {
+ if (ffdi.DeviceType == FILE_DEVICE_NAMED_PIPE)
+ file_type = internal ? virt_blk : virt_pipe;
+ else if (ffdi.DeviceType == FILE_DEVICE_DISK
+ || ffdi.DeviceType == FILE_DEVICE_CD_ROM
+ || ffdi.DeviceType == FILE_DEVICE_VIRTUAL_DISK)
+ {
+ /* Check for file attributes. If we get them, we peeked
+ into a real FS through /proc/sys. */
+ status = NtQueryInformationFile (h, &io, &fbi, sizeof fbi,
+ FileBasicInformation);
+ debug_printf ("NtQueryInformationFile: %y", status);
+ if (!NT_SUCCESS (status))
+ file_type = virt_blk;
+ else
+ file_type = (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ ? virt_fsdir : virt_fsfile;
+ }
+ }
+ NtClose (h);
+ }
+ /* That's it. Return type we found above. */
+ return file_type;
+}
+
+virtual_ftype_t
+fhandler_procsys::exists ()
+{
+ return exists (NULL);
+}
+
+fhandler_procsys::fhandler_procsys ():
+ fhandler_virtual ()
+{
+}
+
+#define UNREADABLE_SYMLINK_CONTENT "<access denied>"
+
+bool
+fhandler_procsys::fill_filebuf ()
+{
+ char *fnamep;
+ UNICODE_STRING path, target;
+ OBJECT_ATTRIBUTES attr;
+ NTSTATUS status;
+ HANDLE h;
+ tmp_pathbuf tp;
+ size_t len;
+
+ mk_unicode_path (&path);
+ if (path.Buffer[path.Length / sizeof (WCHAR) - 1] == L'\\')
+ path.Length -= sizeof (WCHAR);
+ InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL);
+ status = NtOpenSymbolicLinkObject (&h, SYMBOLIC_LINK_QUERY, &attr);
+ if (!NT_SUCCESS (status))
+ goto unreadable;
+ RtlInitEmptyUnicodeString (&target, tp.w_get (),
+ (NT_MAX_PATH - 1) * sizeof (WCHAR));
+ status = NtQuerySymbolicLinkObject (h, &target, NULL);
+ NtClose (h);
+ if (!NT_SUCCESS (status))
+ goto unreadable;
+ len = sys_wcstombs (NULL, 0, target.Buffer, target.Length / sizeof (WCHAR));
+ filebuf = (char *) crealloc_abort (filebuf, procsys_len + len + 1);
+ sys_wcstombs (fnamep = stpcpy (filebuf, procsys), len + 1, target.Buffer,
+ target.Length / sizeof (WCHAR));
+ while ((fnamep = strchr (fnamep, '\\')))
+ *fnamep = '/';
+ return true;
+
+unreadable:
+ filebuf = (char *) crealloc_abort (filebuf,
+ sizeof (UNREADABLE_SYMLINK_CONTENT));
+ strcpy (filebuf, UNREADABLE_SYMLINK_CONTENT);
+ return false;
+}
+
+int
+fhandler_procsys::fstat (struct stat *buf)
+{
+ const char *path = get_name ();
+ debug_printf ("fstat (%s)", path);
+
+ fhandler_base::fstat (buf);
+ /* Best bet. */
+ buf->st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
+ buf->st_uid = 544;
+ buf->st_gid = 18;
+ buf->st_dev = buf->st_rdev = dev ();
+ buf->st_ino = get_ino ();
+ switch (exists (buf))
+ {
+ case virt_directory:
+ case virt_rootdir:
+ case virt_fsdir:
+ buf->st_mode |= S_IFDIR;
+ if (buf->st_mode & S_IRUSR)
+ buf->st_mode |= S_IXUSR;
+ if (buf->st_mode & S_IRGRP)
+ buf->st_mode |= S_IXGRP;
+ if (buf->st_mode & S_IROTH)
+ buf->st_mode |= S_IXOTH;
+ break;
+ case virt_file:
+ case virt_fsfile:
+ buf->st_mode |= S_IFREG;
+ break;
+ case virt_symlink:
+ buf->st_mode |= S_IFLNK;
+ break;
+ case virt_pipe:
+ buf->st_mode |= S_IFIFO;
+ break;
+ case virt_socket:
+ buf->st_mode |= S_IFSOCK;
+ break;
+ case virt_chr:
+ buf->st_mode |= S_IFCHR;
+ break;
+ case virt_blk:
+ buf->st_mode |= S_IFBLK;
+ break;
+ default:
+ set_errno (ENOENT);
+ return -1;
+ }
+ return 0;
+}
+
+DIR *
+fhandler_procsys::opendir (int fd)
+{
+ UNICODE_STRING path;
+ OBJECT_ATTRIBUTES attr;
+ NTSTATUS status;
+ HANDLE dir_hdl;
+ DIR *dir;
+
+ mk_unicode_path (&path);
+ InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL);
+ status = NtOpenDirectoryObject (&dir_hdl, DIRECTORY_QUERY, &attr);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ return NULL;
+ }
+
+ void *dbi_buf = NULL;
+ ULONG size = 65536;
+ ULONG context = 0;
+ int iter;
+ for (iter = 0; iter <= 3; ++iter) /* Allows for a 512K buffer */
+ {
+ void *new_buf = realloc (dbi_buf, size);
+ if (!new_buf)
+ goto err;
+ dbi_buf = new_buf;
+ status = NtQueryDirectoryObject (dir_hdl, dbi_buf, size, FALSE, TRUE,
+ &context, NULL);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_nt_status (status);
+ goto err;
+ }
+ if (status != STATUS_MORE_ENTRIES)
+ break;
+ size <<= 1;
+ }
+ if (iter > 3)
+ {
+ __seterrno_from_nt_status (STATUS_INSUFFICIENT_RESOURCES);
+ goto err;
+ }
+ if (!(dir = fhandler_virtual::opendir (fd)))
+ goto err;
+ /* Note that dir->__handle points to the buffer, it does NOT contain an
+ actual handle! */
+ dir->__handle = dbi_buf;
+ /* dir->__d_internal contains the number of objects returned in the buffer. */
+ dir->__d_internal = context;
+ return dir;
+
+err:
+ NtClose (dir_hdl);
+ free (dbi_buf);
+ return NULL;
+}
+
+int
+fhandler_procsys::readdir (DIR *dir, dirent *de)
+{
+ PDIRECTORY_BASIC_INFORMATION dbi;
+ int res = EBADF;
+
+ if (dir->__handle != INVALID_HANDLE_VALUE)
+ {
+ dbi = ((PDIRECTORY_BASIC_INFORMATION) dir->__handle);
+ dbi += dir->__d_position;
+ if (dir->__d_position >= (__int32_t) dir->__d_internal
+ || dbi->ObjectName.Length == 0)
+ res = ENMFILE;
+ else
+ {
+ sys_wcstombs (de->d_name, NAME_MAX + 1, dbi->ObjectName.Buffer,
+ dbi->ObjectName.Length / sizeof (WCHAR));
+ de->d_ino = hash_path_name (get_ino (), de->d_name);
+ if (RtlEqualUnicodeString (&dbi->ObjectTypeName, &ro_u_natdir, FALSE))
+ de->d_type = DT_DIR;
+ else if (RtlEqualUnicodeString (&dbi->ObjectTypeName, &ro_u_natsyml,
+ FALSE))
+ de->d_type = DT_LNK;
+ else if (!RtlEqualUnicodeString (&dbi->ObjectTypeName, &ro_u_natdev,
+ FALSE))
+ de->d_type = DT_CHR;
+ else /* Can't nail down "Device" objects without further testing. */
+ de->d_type = DT_UNKNOWN;
+ ++dir->__d_position;
+ res = 0;
+ }
+ }
+ syscall_printf ("%d = readdir(%p, %p)", res, dir, de);
+ return res;
+}
+
+long
+fhandler_procsys::telldir (DIR *dir)
+{
+ return dir->__d_position;
+}
+
+void
+fhandler_procsys::seekdir (DIR *dir, long pos)
+{
+ if (pos < 0)
+ dir->__d_position = 0;
+ else if (pos > (__int32_t) dir->__d_internal)
+ dir->__d_position = (__int32_t) dir->__d_internal;
+ else
+ dir->__d_position = pos;
+}
+
+int
+fhandler_procsys::closedir (DIR *dir)
+{
+ if (dir->__handle != INVALID_HANDLE_VALUE)
+ {
+ free (dir->__handle);
+ dir->__handle = INVALID_HANDLE_VALUE;
+ }
+ return fhandler_virtual::closedir (dir);
+}
+
+void
+fhandler_procsys::read (void *ptr, size_t& len)
+{
+ fhandler_base::raw_read (ptr, len);
+}
+
+ssize_t
+fhandler_procsys::write (const void *ptr, size_t len)
+{
+ return fhandler_base::raw_write (ptr, len);
+}
+
+int
+fhandler_procsys::open (int flags, mode_t mode)
+{
+ int res = 0;
+
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ set_errno (EINVAL);
+ else
+ {
+ switch (exists (NULL))
+ {
+ case virt_directory:
+ case virt_rootdir:
+ if ((flags & O_ACCMODE) != O_RDONLY)
+ set_errno (EISDIR);
+ else
+ {
+ nohandle (true);
+ res = 1;
+ }
+ break;
+ case virt_none:
+ set_errno (ENOENT);
+ break;
+ default:
+ res = fhandler_base::open (flags, mode);
+ break;
+ }
+ }
+ syscall_printf ("%d = fhandler_procsys::open(%p, 0%o)", res, flags, mode);
+ return res;
+}
+
+int
+fhandler_procsys::close ()
+{
+ if (!nohandle ())
+ NtClose (get_handle ());
+ return fhandler_virtual::close ();
+}
+#if 0
+int
+fhandler_procsys::ioctl (unsigned int cmd, void *)
+{
+}
+#endif