diff options
Diffstat (limited to 'winsup/cygwin/fhandler/procnet.cc')
-rw-r--r-- | winsup/cygwin/fhandler/procnet.cc | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler/procnet.cc b/winsup/cygwin/fhandler/procnet.cc new file mode 100644 index 000000000..d512887d5 --- /dev/null +++ b/winsup/cygwin/fhandler/procnet.cc @@ -0,0 +1,262 @@ +/* fhandler_procnet.cc: fhandler for /proc/net virtual filesystem + +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. */ + +#define __INSIDE_CYGWIN_NET__ +#define USE_SYS_TYPES_FD_SET +#include "winsup.h" +/* 2014-04-24: Current Mingw headers define sockaddr_in6 using u_long (8 byte) + because a redefinition for LP64 systems is missing. This leads to a wrong + definition and size of sockaddr_in6 when building with winsock headers. */ +#undef u_long +#define u_long __ms_u_long +#include <w32api/ws2tcpip.h> +#include <w32api/iphlpapi.h> +#include <asm/byteorder.h> +#include <stdio.h> +#include "cygerrno.h" +#include "path.h" +#include "fhandler.h" +#include "fhandler_virtual.h" +#include "dtable.h" + +bool get_adapters_addresses (PIP_ADAPTER_ADDRESSES *pa0, ULONG family); + +static off_t format_procnet_ifinet6 (void *, char *&); + +static const virt_tab_t procnet_tab[] = +{ + { _VN ("."), FH_PROCNET, virt_directory, NULL }, + { _VN (".."), FH_PROCNET, virt_directory, NULL }, + { _VN ("if_inet6"), FH_PROCNET, virt_file, format_procnet_ifinet6 }, + { NULL, 0, FH_NADA, virt_none, NULL } +}; + +static const int PROCNET_LINK_COUNT = + (sizeof (procnet_tab) / sizeof (virt_tab_t)) - 1; + +virtual_ftype_t +fhandler_procnet::exists () +{ + const char *path = get_name (); + debug_printf ("exists (%s)", path); + path += proc_len + 1; + while (*path != 0 && !isdirsep (*path)) + path++; + if (*path == 0) + return virt_rootdir; + + virt_tab_t *entry = virt_tab_search (path + 1, false, procnet_tab, + PROCNET_LINK_COUNT); + if (entry) + { + if (entry->type == virt_file && !get_adapters_addresses (NULL, AF_INET6)) + return virt_none; + fileid = entry - procnet_tab; + return entry->type; + } + return virt_none; +} + +fhandler_procnet::fhandler_procnet (): + fhandler_proc () +{ +} + +int +fhandler_procnet::fstat (struct stat *buf) +{ + fhandler_base::fstat (buf); + buf->st_mode &= ~_IFMT & NO_W; + int file_type = exists (); + switch (file_type) + { + case virt_none: + set_errno (ENOENT); + return -1; + case virt_directory: + case virt_rootdir: + buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + buf->st_nlink = 2; + return 0; + case virt_file: + default: + buf->st_mode |= S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + return 0; + } +} + +int +fhandler_procnet::readdir (DIR *dir, dirent *de) +{ + int res = ENMFILE; + if (dir->__d_position >= PROCNET_LINK_COUNT) + goto out; + if (procnet_tab[dir->__d_position].type == virt_file + && !get_adapters_addresses (NULL, AF_INET6)) + goto out; + strcpy (de->d_name, procnet_tab[dir->__d_position].name); + de->d_type = virt_ftype_to_dtype (procnet_tab[dir->__d_position].type); + dir->__d_position++; + dir->__flags |= dirent_saw_dot | dirent_saw_dot_dot; + res = 0; +out: + syscall_printf ("%d = readdir(%p, %p) (%s)", res, dir, de, de->d_name); + return res; +} + +int +fhandler_procnet::open (int flags, mode_t mode) +{ + int res = fhandler_virtual::open (flags, mode); + if (!res) + goto out; + + nohandle (true); + + const char *path; + path = get_name () + proc_len + 1; + while (*path != 0 && !isdirsep (*path)) + path++; + + if (*path == 0) + { + if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + { + set_errno (EEXIST); + res = 0; + goto out; + } + else if (flags & O_WRONLY) + { + set_errno (EISDIR); + res = 0; + goto out; + } + else + { + diropen = true; + goto success; + } + } + + virt_tab_t *entry; + entry = virt_tab_search (path + 1, true, procnet_tab, PROCNET_LINK_COUNT); + if (!entry) + { + set_errno ((flags & O_CREAT) ? EROFS : ENOENT); + res = 0; + goto out; + } + if (flags & O_WRONLY) + { + set_errno (EROFS); + res = 0; + goto out; + } + + fileid = entry - procnet_tab; + if (!fill_filebuf ()) + { + res = 0; + goto out; + } + + if (flags & O_APPEND) + position = filesize; + else + position = 0; + +success: + res = 1; + set_flags ((flags & ~O_TEXT) | O_BINARY); + set_open_status (); +out: + syscall_printf ("%d = fhandler_proc::open(%y, 0%o)", res, flags, mode); + return res; +} + +bool +fhandler_procnet::fill_filebuf () +{ + if (procnet_tab[fileid].format_func) + { + filesize = procnet_tab[fileid].format_func (NULL, filebuf); + return true; + } + return false; +} + +/* Return the same scope values as Linux. */ +static unsigned int +get_scope (struct in6_addr *addr) +{ + if (IN6_IS_ADDR_LOOPBACK (addr)) + return 0x10; + if (IN6_IS_ADDR_LINKLOCAL (addr)) + return 0x20; + if (IN6_IS_ADDR_SITELOCAL (addr)) + return 0x40; + if (IN6_IS_ADDR_V4COMPAT (addr)) + return 0x80; + return 0x0; +} + +/* Convert DAD state into Linux compatible values. */ +static unsigned int dad_to_flags[] = +{ + 0x02, /* Invalid -> NODAD */ + 0x40, /* Tentative -> TENTATIVE */ + 0xc0, /* Duplicate -> PERMANENT | TENTATIVE */ + 0x20, /* Deprecated -> DEPRECATED */ + 0x80 /* Preferred -> PERMANENT */ +}; + +static off_t +format_procnet_ifinet6 (void *, char *&filebuf) +{ + PIP_ADAPTER_ADDRESSES pa0 = NULL, pap; + PIP_ADAPTER_UNICAST_ADDRESS pua; + ULONG alloclen; + off_t filesize = 0; + + if (!get_adapters_addresses (&pa0, AF_INET6)) + goto out; + alloclen = 0; + for (pap = pa0; pap; pap = pap->Next) + for (pua = pap->FirstUnicastAddress; pua; pua = pua->Next) + alloclen += 100; + if (!alloclen) + goto out; + filebuf = (char *) crealloc (filebuf, alloclen); + if (!filebuf) + goto out; + for (pap = pa0; pap; pap = pap->Next) + for (pua = pap->FirstUnicastAddress; pua; pua = pua->Next) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) + pua->Address.lpSockaddr; + for (int i = 0; i < 8; ++i) + /* __small_sprintf generates upper-case hex. */ + filesize += sprintf (filebuf + filesize, "%04x", + ntohs (sin6->sin6_addr.s6_addr16[i])); + filebuf[filesize++] = ' '; + filesize += sprintf (filebuf + filesize, + "%02lx %02x %02x %02x %s\n", + (long) pap->Ipv6IfIndex, + pua->OnLinkPrefixLength, + get_scope (&((struct sockaddr_in6 *) + pua->Address.lpSockaddr)->sin6_addr), + dad_to_flags [pua->DadState], + pap->AdapterName); + } + +out: + if (pa0) + free (pa0); + return filesize; +} |