4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-01-19 12:59:21 +08:00
Takashi Yano 10477d95ec Cygwin: console: Fix race issue on allocating console simultaneously.
Previously, if two or more processes request to allocate a console at
the same time, the same device number could be accidentally allocated.
This patch fixes the issue by creating a named mutex (input_mutex) and
checking if GetLastError() is ERROR_ALREADY_EXIST or not to ensure
the unit number will be allocated in an atomic way. Thanks to this,
EnumWindow() is not necessary anymore to scan console units.

This also makes minor device numbers unique and console devices visible
across sessions. This disallows duplicated device number for different
sessions, so the MAX_CONS_DEV has been increased from 64 to 128.

Additionally, the console allocation and scanning will be faster as
a side effect of eliminating EnumWindows().

Fixes: 3721a756b0d8 ("Cygwin: console: Make the console accessible from other terminals.")
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
2024-07-09 00:01:22 +09:00

387 lines
10 KiB
Plaintext

%import {
#include "winsup.h"
#include "devices.h"
#include "sys/cygwin.h"
#include "tty.h"
#include "pinfo.h"
#include "shared_info.h"
#include "path.h"
#include "fhandler.h"
#include "ntdll.h"
#include "dtable.h"
#include "cygheap.h"
typedef const _device *KR_device_t;
}
%type KR_device_t
%local {
static int
exists_internal (const device&)
{
return false;
}
static int
exists (const device&)
{
return true;
}
/* Check existence of POSIX devices backed by real NT devices. */
static int
exists_ntdev (const device& dev)
{
WCHAR wpath[MAX_PATH];
UNICODE_STRING upath;
OBJECT_ATTRIBUTES attr;
HANDLE h;
NTSTATUS status;
sys_mbstowcs (wpath, MAX_PATH, dev.native ());
RtlInitUnicodeString (&upath, wpath);
InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
/* Except for the serial IO devices, the native paths are
direct device paths, not symlinks, so every status code
except for "NOT_FOUND" means the device exists. */
status = NtOpenSymbolicLinkObject (&h, SYMBOLIC_LINK_QUERY, &attr);
switch (status)
{
case STATUS_OBJECT_NAME_NOT_FOUND:
case STATUS_OBJECT_PATH_NOT_FOUND:
return false;
case STATUS_SUCCESS:
NtClose (h);
default:
break;
}
return true;
}
/* Don't list via readdir but allow as a direct reference. */
static int
exists_ntdev_silent (const device& dev)
{
return exists_ntdev (dev) ? -1 : false;
}
static int
exists_console (const device& dev)
{
fh_devices devn = *const_cast<device *> (&dev);
switch (devn)
{
case FH_CONSOLE:
case FH_CONIN:
case FH_CONOUT:
return cygheap && cygheap->ctty && cygheap->ctty->is_console ()
&& fhandler_console::exists ();
default:
if (dev.get_minor () < MAX_CONS_DEV)
{
int n = fhandler_console::console_unit (dev.get_minor ());
return (n == dev.get_minor ());
}
return false;
}
}
static int
exists_pty (const device& dev)
{
/* Only existing slave ptys. */
return cygwin_shared->tty.connect (dev.get_minor ()) != -1;
}
const _device dev_cygdrive_storage =
{"/cygdrive", {FH_CYGDRIVE}, "", exists};
const _device dev_fs_storage =
{"", {FH_FS}, "", exists};
const _device dev_dev_disk_storage =
{"", {FH_DEV_DISK}, "", exists};
const _device dev_proc_storage =
{"", {FH_PROC}, "", exists};
const _device dev_procnet_storage =
{"", {FH_PROCNET}, "", exists};
const _device dev_procsys_storage =
{"", {FH_PROCSYS}, "", exists};
const _device dev_procsysvipc_storage =
{"", {FH_PROCSYSVIPC}, "", exists};
const _device dev_netdrive_storage =
{"", {FH_NETDRIVE}, "", exists};
const _device dev_registry_storage =
{"", {FH_REGISTRY}, "", exists_internal};
const _device dev_piper_storage =
{"", {FH_PIPER}, "", exists_internal};
const _device dev_pipew_storage =
{"", {FH_PIPEW}, "", exists_internal};
const _device dev_signalfd_storage =
{"", {FH_SIGNALFD}, "", exists_internal};
const _device dev_timerfd_storage =
{"", {FH_TIMERFD}, "", exists_internal};
const _device dev_mqueue_storage =
{"", {FH_MQUEUE}, "", exists_internal};
const _device dev_socket_storage =
{"", {FH_SOCKET}, "", exists_internal};
const _device dev_af_inet_storage =
{"", {FH_INET}, "", exists_internal};
const _device dev_af_unix_storage =
{"", {FH_UNIX}, "", exists_internal};
const _device dev_af_local_storage =
{"", {FH_LOCAL}, "", exists_internal};
const _device dev_bad_storage =
{"", {FH_NADA}, "", exists_internal};
const _device dev_error_storage =
{"", {FH_ERROR}, "", exists_internal};
#define BRACK(x) {devn: x}
%storage_here
}
/* Internal devices below are prefixed with a ":". This moves them out of
the POSIX namespace. */
%%
"/dev", BRACK(FH_DEV), "", exists, S_IFDIR
"/dev/disk", BRACK(FH_DEV_DISK), "", exists, S_IFDIR
"/dev/tty", BRACK(FH_TTY), "/dev/tty", exists, S_IFCHR
"/dev/pty%(0-127)d", BRACK(FHDEV(DEV_PTYS_MAJOR, {$1})), "/dev/pty{$1}", exists_pty, S_IFCHR, =ptys_dev
":ptym%(0-127)d", BRACK(FHDEV(DEV_PTYM_MAJOR, {$1})), "/dev/ptym{$1}", exists_internal, S_IFCHR, =ptym_dev
"/dev/cons%(0-127)d", BRACK(FHDEV(DEV_CONS_MAJOR, {$1})), "/dev/cons{$1}", exists_console, S_IFCHR, =cons_dev
"/dev/console", BRACK(FH_CONSOLE), "/dev/console", exists_console, S_IFCHR, =console_dev
"/dev/ptmx", BRACK(FH_PTMX), "/dev/ptmx", exists, S_IFCHR
"/dev/windows", BRACK(FH_WINDOWS), "\\Device\\Null", exists_ntdev, S_IFCHR
"/dev/mixer", BRACK(FH_OSS_MIXER), "\\Device\\Null", exists_ntdev, S_IFCHR
"/dev/dsp", BRACK(FH_OSS_DSP), "\\Device\\Null", exists_ntdev, S_IFCHR
"/dev/conin", BRACK(FH_CONIN), "/dev/conin", exists_console, S_IFCHR
"/dev/conout", BRACK(FH_CONOUT), "/dev/conout", exists_console, S_IFCHR
"/dev/null", BRACK(FH_NULL), "\\Device\\Null", exists_ntdev, S_IFCHR
"/dev/zero", BRACK(FH_ZERO), "\\Device\\Null", exists_ntdev, S_IFCHR
"/dev/full", BRACK(FH_FULL), "\\Device\\Null", exists_ntdev, S_IFCHR
"/dev/random", BRACK(FH_RANDOM), "\\Device\\Null", exists_ntdev, S_IFCHR
"/dev/urandom", BRACK(FH_URANDOM), "\\Device\\Null", exists_ntdev, S_IFCHR, =urandom_dev
"/dev/clipboard", BRACK(FH_CLIPBOARD), "\\Device\\Null", exists_ntdev, S_IFCHR
"/dev/com%(1-16)d", BRACK(FHDEV(DEV_SERIAL_MAJOR, {$1 - 1})), "\\??\\COM{$1}", exists_ntdev_silent, S_IFCHR
"/dev/ttyS%(0-127)d", BRACK(FHDEV(DEV_SERIAL_MAJOR, {$1})), "\\??\\COM{$1 + 1}", exists_ntdev, S_IFCHR
":pipe", BRACK(FH_PIPE), "/dev/pipe", exists_internal, S_IFCHR
":fifo", BRACK(FH_FIFO), "/dev/fifo", exists_internal, S_IFCHR
"/dev/st%(0-127)d", BRACK(FHDEV(DEV_TAPE_MAJOR, {$1})), "\\Device\\Tape{$1}", exists_ntdev, S_IFCHR
"/dev/nst%(0-127)d", BRACK(FHDEV(DEV_TAPE_MAJOR, {$1 + 128})), "\\Device\\Tape{$1}", exists_ntdev, S_IFCHR
"/dev/fd%(0-15)d", BRACK(FHDEV(DEV_FLOPPY_MAJOR, {$1})), "\\Device\\Floppy{$1}", exists_ntdev, S_IFBLK
"/dev/scd%(0-15)d", BRACK(FHDEV(DEV_CDROM_MAJOR, {$1})), "\\Device\\CdRom{$1}", exists_ntdev, S_IFBLK
"/dev/sr%(0-15)d", BRACK(FHDEV(DEV_CDROM_MAJOR, {$1})), "\\Device\\CdRom{$1}", exists_ntdev, S_IFBLK
"/dev/fd", BRACK(FH_DEV_FD), "/proc/self/fd", exists, S_IFLNK
"/dev/stdin", BRACK(FH_DEV_FD), "/proc/self/fd/0", exists, S_IFLNK
"/dev/stdout", BRACK(FH_DEV_FD), "/proc/self/fd/1", exists, S_IFLNK
"/dev/stderr", BRACK(FH_DEV_FD), "/proc/self/fd/2", exists, S_IFLNK
%other {return NULL;}
%%
#undef BRACK
const _device *dev_storage_end = dev_storage
+ (sizeof dev_storage / sizeof dev_storage[0]);
/* Convert disk/partition to major/minor */
static void
conv_dp_to_mm (int drive, int part, _major_t &major, _minor_t &minor)
{
if (part >= 16)
{
major = DEV_SD_HIGHPART_START + drive / 5;
drive %= 5;
minor = (part - 16) + 48 * drive;
return;
}
if (drive < ('q' - 'a')) /* /dev/sda -to- /dev/sdp */
major = DEV_SD_MAJOR;
else if (drive < 32) /* /dev/sdq -to- /dev/sdaf */
{
major = DEV_SD1_MAJOR;
drive -= 'q' - 'a';
}
else if (drive < 48) /* /dev/sdag -to- /dev/sdav */
{
major = DEV_SD2_MAJOR;
drive -= 32;
}
else if (drive < 64) /* /dev/sdaw -to- /dev/sdbl */
{
major = DEV_SD3_MAJOR;
drive -= 48;
}
else if (drive < 80) /* /dev/sdbm -to- /dev/sdcb */
{
major = DEV_SD4_MAJOR;
drive -= 64;
}
else if (drive < 96) /* /dev/sdcc -to- /dev/sdcr */
{
major = DEV_SD5_MAJOR;
drive -= 80;
}
else if (drive < 112) /* /dev/sdcs -to- /dev/sddh */
{
major = DEV_SD6_MAJOR;
drive -= 96;
}
/* NOTE: This will cause multiple /dev/sddx entries in
/proc/partitions if there are more than 128 devices */
else /* /dev/sddi -to- /dev/sddx */
{
major = DEV_SD7_MAJOR;
drive -= 112;
}
minor = part + (drive * 16);
}
#define DISK_PREFIX "/dev/sd"
#define DP_LEN (sizeof (DISK_PREFIX) - 1)
static const char *hd_pattern = "\\Device\\Harddisk%u\\Partition%u";
void
device::parse (const char *s)
{
size_t len = strlen (s);
const _device *dev = KR_find_keyword (s, len);
if (!dev)
{
/* /dev/sd* devices have 8192 entries, given that we support 128 disks
with up to 64 partitions. Handling them with shilka raises the size
of devices.o from ~250K to ~2 Megs. So we handle them here manually
to save this space. */
int drive = 0, part = 0;
const char *pos = s + DP_LEN;
/* Generic check for /dev/sd[a-z] prefix */
if (len <= DP_LEN || strncmp (s, DISK_PREFIX, DP_LEN)
|| pos[0] < 'a' || pos[0] > 'z')
goto no_disk;
/* /dev/sdd[a-x]? */
if (pos[0] == 'd' && pos[1] >= 'a' && pos[1] <= 'x')
{
drive = 104 + (pos[1] - 'a');
++pos;
}
/* /dev/sd[a-c][a-z]? */
else if (pos[0] <= 'c' && pos[1] >= 'a' && pos[1] <= 'z')
{
drive = 26 + (pos[0] - 'a') * 26 + (pos[1] - 'a');
++pos;
}
else
drive = (pos[0] - 'a');
/* Check next position in string for partition number. */
++pos;
/* No partition number, equivalent to Windows partition 0. */
if (!pos[0])
;
/* First digit must not be 0. */
else if (pos[0] < '1' || pos[0] > '9')
goto no_disk;
else if (!pos[1])
part = (pos[0] - '0');
else if (pos[1] < '0' || pos[1] > '9' || pos[2] != '\0')
goto no_disk;
else
{
part = (pos[0] - '0') * 10 + (pos[1] - '0');
if (part > 63)
goto no_disk;
}
char buf[sizeof *hd_pattern + 32];
__small_sprintf (buf, hd_pattern, drive, part);
native (buf, false);
if (exists_ntdev (*this))
{
name (s, true);
conv_dp_to_mm (drive, part, d.major, d.minor);
native (buf, true);
exists_func = exists_ntdev;
_mode = S_IFBLK;
lives_in_dev = dev_on_fs = false;
return;
}
no_disk:
*this = *fs_dev;
}
else
*this = *dev;
}
void
device::init ()
{
/* nothing to do... yet */
}
void
device::parse (_major_t major, _minor_t minor)
{
dev_t devn = FHDEV (major, minor);
d.devn = 0;
for (const _device *devidx = dev_storage; devidx < dev_storage_end; devidx++)
if (devidx->d.devn == devn)
{
*this = *devidx;
break;
}
if (!*this)
d.devn = FHDEV (major, minor);
}
void
device::parse (dev_t dev)
{
parse (_major (dev), _minor (dev));
}
void
device::parsedisk (int drive, int part)
{
char buf[sizeof ("/dev/sddx63")], *bp;
conv_dp_to_mm (drive, part, d.major, d.minor);
bp = stpcpy (buf, "/dev/sd");
if (drive >= 26)
{
drive -= 26;
*bp++ = drive / 26 + 'a';
drive %= 26;
}
*bp++ = drive + 'a';
if (part)
{
if (part >= 10)
{
*bp++ = part / 10 + '0';
part %= 10;
}
*bp++ = part + '0';
}
*bp = '\0';
name (buf, true);
}