/* fhandler_dev.cc, Implement /dev. Copyright 2012 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 "path.h" #include "fhandler.h" #include "shared_info.h" #include "ntdll.h" #include "dtable.h" #include "cygheap.h" #include "devices.h" #define _COMPILING_NEWLIB #include <dirent.h> #define dev_prefix_len (sizeof ("/dev")) #define dev_storage_scan_start (dev_storage + 1) #define dev_storage_size (dev_storage_end - dev_storage_scan_start) static int device_cmp (const void *a, const void *b) { return strcmp (((const device *) a)->name, ((const device *) b)->name + dev_prefix_len); } fhandler_dev::fhandler_dev () : fhandler_disk_file (), devidx (NULL), dir_exists (true) { } DIR * fhandler_dev::opendir (int fd) { DIR *dir; DIR *res = NULL; dir = fhandler_disk_file::opendir (fd); if (dir) return dir; if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL) set_errno (ENOMEM); else if ((dir->__d_dirent = (struct dirent *) malloc (sizeof (struct dirent))) == NULL) { set_errno (ENOMEM); goto free_dir; } else { cygheap_fdnew cfd; if (cfd < 0 && fd < 0) goto free_dirent; dir->__d_dirname = NULL; dir->__d_dirent->__d_version = __DIRENT_VERSION; dir->__d_cookie = __DIRENT_COOKIE; dir->__handle = INVALID_HANDLE_VALUE; dir->__d_position = 0; dir->__flags = 0; dir->__d_internal = 0; if (fd >= 0) dir->__d_fd = fd; else { cfd = this; dir->__d_fd = cfd; cfd->nohandle (true); } set_close_on_exec (true); dir->__fh = this; devidx = dev_storage_scan_start; res = dir; } syscall_printf ("%p = opendir (%s)", res, get_name ()); return res; free_dirent: free (dir->__d_dirent); free_dir: free (dir); return res; } int fhandler_dev::readdir (DIR *dir, dirent *de) { int ret; device dev; if (!devidx) { while ((ret = fhandler_disk_file::readdir (dir, de)) == 0) { /* Avoid to print devices for which users have created files under /dev already, for instance by using the old script from Igor Peshansky. */ dev.name = de->d_name; if (!bsearch (&dev, dev_storage_scan_start, dev_storage_size, sizeof dev, device_cmp)) break; } if (ret == ENMFILE) devidx = dev_storage_scan_start; else goto out; } /* Now start processing our internal dev table. */ ret = ENMFILE; while (devidx < dev_storage_end) { const device& thisdev = *devidx++; if (!thisdev.expose ()) continue; int devn = *const_cast<device *> (&thisdev); /* Exclude devices which are only available for internal purposes and devices which are not really existing at this time. */ switch (thisdev.get_major ()) { case DEV_PTYS_MAJOR: /* Show only existing slave ptys. */ if (cygwin_shared->tty.connect (thisdev.get_minor ()) == -1) continue; break; case DEV_CONS_MAJOR: /* Show only the one console which is our controlling tty right now. */ if (!iscons_dev (myself->ctty) || myself->ctty != devn) continue; break; case DEV_TTY_MAJOR: /* Show con{in,out,sole} only if we're running in a console. */ switch (devn) { case FH_CONIN: case FH_CONOUT: case FH_CONSOLE: if (!iscons_dev (myself->ctty)) continue; } break; case DEV_SERIAL_MAJOR: case DEV_FLOPPY_MAJOR: case DEV_TAPE_MAJOR: case DEV_CDROM_MAJOR: case DEV_SD_MAJOR: case DEV_SD1_MAJOR: case DEV_SD2_MAJOR: case DEV_SD3_MAJOR: case DEV_SD4_MAJOR: case DEV_SD5_MAJOR: case DEV_SD6_MAJOR: case DEV_SD7_MAJOR: /* Check existence of POSIX devices backed by real NT devices. */ { WCHAR wpath[MAX_PATH]; UNICODE_STRING upath; OBJECT_ATTRIBUTES attr; HANDLE h; NTSTATUS status; sys_mbstowcs (wpath, MAX_PATH, thisdev.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: continue; case STATUS_SUCCESS: NtClose (h); break; default: break; } } break; } ++dir->__d_position; strcpy (de->d_name, thisdev.name + dev_prefix_len); de->d_ino = hash_path_name (0, thisdev.native); switch (thisdev.get_major ()) { case DEV_FLOPPY_MAJOR: case DEV_TAPE_MAJOR: case DEV_CDROM_MAJOR: case DEV_SD_MAJOR: case DEV_SD1_MAJOR: case DEV_SD2_MAJOR: case DEV_SD3_MAJOR: case DEV_SD4_MAJOR: case DEV_SD5_MAJOR: case DEV_SD6_MAJOR: case DEV_SD7_MAJOR: de->d_type = DT_BLK; break; case DEV_TTY_MAJOR: switch (devn) { case FH_CONIN: case FH_CONOUT: case FH_CONSOLE: dev.parse (myself->ctty); de->d_ino = hash_path_name (0, dev.native); break; } /*FALLTHRU*/ default: de->d_type = DT_CHR; break; } ret = 0; break; } out: debug_printf ("returning %d", ret); return ret; } void fhandler_dev::rewinddir (DIR *dir) { devidx = dir_exists ? NULL : dev_storage_scan_start; fhandler_disk_file::rewinddir (dir); }