diff options
Diffstat (limited to 'winsup/cygwin/fhandler_procsys.cc')
-rw-r--r-- | winsup/cygwin/fhandler_procsys.cc | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler_procsys.cc b/winsup/cygwin/fhandler_procsys.cc new file mode 100644 index 000000000..aa0b6b699 --- /dev/null +++ b/winsup/cygwin/fhandler_procsys.cc @@ -0,0 +1,422 @@ +/* fhandler_procsys.cc: fhandler for native NT namespace. + + Copyright 2010 Red Hat, Inc. + +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); \ + } + +/* Returns 0 if path doesn't exist, >0 if path is a directory, + -1 if path is a file, -2 if it's a symlink. */ +virtual_ftype_t +fhandler_procsys::exists (struct __stat64 *buf) +{ + UNICODE_STRING path; \ + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE h; + FILE_BASIC_INFORMATION fbi; + virtual_ftype_t file_type = virt_chr; + + if (strlen (get_name ()) == procsys_len) + return virt_rootdir; + mk_unicode_path (&path); + /* First try to open as file/device to get more info. */ + InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL); + status = NtOpenFile (&h, READ_CONTROL | FILE_READ_ATTRIBUTES, &attr, &io, + FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT); + if (status == STATUS_OBJECT_PATH_NOT_FOUND) + return virt_none; + /* If the name isn't found, or we get this dreaded sharing violation, let + the caller try again as normal file. */ + if (status == STATUS_OBJECT_NAME_NOT_FOUND + || status == STATUS_NO_MEDIA_IN_DEVICE + || status == STATUS_SHARING_VIOLATION) + return virt_fsfile; /* Just try again as normal file. */ + /* Check for pipe errors, which make a good hint... */ + if (status >= STATUS_PIPE_NOT_AVAILABLE && status <= STATUS_PIPE_BUSY) + file_type = virt_pipe; + else if (status == STATUS_ACCESS_DENIED) + { + /* Check if this is just some file or dir on a real FS to circumvent + most permission problems. */ + status = NtQueryAttributesFile (&attr, &fbi); + if (NT_SUCCESS (status)) + return (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ? virt_fsdir : virt_fsfile; + } + else if (NT_SUCCESS (status)) + { + NTSTATUS dev_stat; + FILE_FS_DEVICE_INFORMATION ffdi; + + /* If requested, check permissions. */ + if (buf) + get_object_attribute (h, &buf->st_uid, &buf->st_gid, &buf->st_mode); + /* Check for the device type. */ + dev_stat = NtQueryVolumeInformationFile (h, &io, &ffdi, sizeof ffdi, + FileFsDeviceInformation); + /* And 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); + NtClose (h); + if (NT_SUCCESS (dev_stat)) + { + if (ffdi.DeviceType == FILE_DEVICE_NAMED_PIPE) + file_type = NT_SUCCESS (status) ? virt_pipe : virt_blk; + else if (NT_SUCCESS (status)) + file_type = (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ? virt_fsdir : virt_fsfile; + else if (ffdi.DeviceType == FILE_DEVICE_DISK + || ffdi.DeviceType == FILE_DEVICE_CD_ROM + || ffdi.DeviceType == FILE_DEVICE_DFS + || ffdi.DeviceType == FILE_DEVICE_VIRTUAL_DISK) + file_type = virt_blk; + } + } + /* Then check if it's a symlink. */ + status = NtOpenSymbolicLinkObject (&h, READ_CONTROL | SYMBOLIC_LINK_QUERY, + &attr); + 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; + } + /* Eventually, test if it's an object directory. */ + status = NtOpenDirectoryObject (&h, READ_CONTROL | DIRECTORY_QUERY, &attr); + 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; + /* Give up. Just treat as character device. */ + return file_type; +} + +virtual_ftype_t +fhandler_procsys::exists () +{ + return exists (NULL); +} + +fhandler_procsys::fhandler_procsys (): + fhandler_virtual () +{ +} + +bool +fhandler_procsys::fill_filebuf () +{ + /* The NT device namespace is ASCII only. */ + char *fnamep; + UNICODE_STRING path, target; + OBJECT_ATTRIBUTES attr; + NTSTATUS status; + HANDLE h; + tmp_pathbuf tp; + + 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)) + return false; + RtlInitEmptyUnicodeString (&target, tp.w_get (), + (NT_MAX_PATH - 1) * sizeof (WCHAR)); + status = NtQuerySymbolicLinkObject (h, &target, NULL); + NtClose (h); + if (!NT_SUCCESS (status)) + return false; + size_t 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; +} + +int +fhandler_procsys::fstat (struct __stat64 *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 ().devn; + 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 h; + DIR *dir = fhandler_virtual::opendir (fd); + + mk_unicode_path (&path); + InitializeObjectAttributes (&attr, &path, OBJ_CASE_INSENSITIVE, NULL, NULL); + status = NtOpenDirectoryObject (&h, DIRECTORY_QUERY, &attr); + if (!NT_SUCCESS (status)) + { + free (dir); + __seterrno_from_nt_status (status); + return NULL; + } + dir->__handle = h; + return dir; +} + +int +fhandler_procsys::readdir (DIR *dir, dirent *de) +{ + NTSTATUS status; + struct fdbi + { + DIRECTORY_BASIC_INFORMATION dbi; + WCHAR buf[2][NAME_MAX + 1]; + } f; + int res = EBADF; + + if (dir->__handle != INVALID_HANDLE_VALUE) + { + BOOLEAN restart = dir->__d_position ? FALSE : TRUE; + status = NtQueryDirectoryObject (dir->__handle, &f, sizeof f, TRUE, + restart, (PULONG) &dir->__d_position, + NULL); + if (!NT_SUCCESS (status)) + res = ENMFILE; + else + { + sys_wcstombs (de->d_name, NAME_MAX + 1, f.dbi.ObjectName.Buffer, + f.dbi.ObjectName.Length / sizeof (WCHAR)); + de->d_ino = hash_path_name (get_ino (), de->d_name); + de->d_type = 0; + 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) +{ + dir->__d_position = pos; +} + +int +fhandler_procsys::closedir (DIR *dir) +{ + if (dir->__handle != INVALID_HANDLE_VALUE) + { + NtClose (dir->__handle); + dir->__handle = INVALID_HANDLE_VALUE; + } + return fhandler_virtual::closedir (dir); +} + +void __stdcall +fhandler_procsys::read (void *ptr, size_t& len) +{ + NTSTATUS status; + IO_STATUS_BLOCK io; + LARGE_INTEGER off = { QuadPart:0LL }; + + status = NtReadFile (get_handle (), NULL, NULL, NULL, &io, ptr, len, + &off, NULL); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + len = -1; + } + else + len = io.Information; +} + +ssize_t __stdcall +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) +{ + UNICODE_STRING path; + OBJECT_ATTRIBUTES attr; + IO_STATUS_BLOCK io; + NTSTATUS status; + HANDLE h; + ULONG access; + ULONG options = FILE_OPEN_FOR_BACKUP_INTENT; + + + int res = fhandler_virtual::open (flags, mode); + if (!res) + goto out; + + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL) || (flags & O_TRUNC)) + { + set_errno (EINVAL); + res = 0; + goto out; + } + mk_unicode_path (&path); + InitializeObjectAttributes (&attr, &path, OBJ_INHERIT | OBJ_CASE_INSENSITIVE, + NULL, NULL); + switch (exists ()) + { + case virt_directory: + case virt_rootdir: + if ((flags & O_ACCMODE) != O_RDONLY) + { + set_errno (EISDIR); + res = 0; + goto out; + } + nohandle (true); + res = 1; + goto out; + default: + break; + } + if ((flags & O_ACCMODE) == O_RDONLY) + access = GENERIC_READ; + else if ((flags & O_ACCMODE) == O_WRONLY) + access = GENERIC_WRITE | READ_CONTROL | FILE_READ_ATTRIBUTES; + else + access = GENERIC_READ | GENERIC_WRITE; + if (flags & O_SYNC) + options |= FILE_WRITE_THROUGH; + if (flags & O_DIRECT) + options |= FILE_NO_INTERMEDIATE_BUFFERING; + if (!(flags & O_NONBLOCK)) + { + access |= SYNCHRONIZE; + options |= FILE_SYNCHRONOUS_IO_NONALERT; + } + status = NtOpenFile (&h, access, &attr, &io, FILE_SHARE_VALID_FLAGS, options); + if (!NT_SUCCESS (status)) + { + __seterrno_from_nt_status (status); + res = 0; + goto out; + } + set_io_handle (h); + set_open_status (); + res = 1; +out: + syscall_printf ("%d = fhandler_procsys::open (%p, %d)", 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 |