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/dev.cc')
-rw-r--r--winsup/cygwin/fhandler/dev.cc275
1 files changed, 275 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler/dev.cc b/winsup/cygwin/fhandler/dev.cc
new file mode 100644
index 000000000..c6bda5654
--- /dev/null
+++ b/winsup/cygwin/fhandler/dev.cc
@@ -0,0 +1,275 @@
+/* fhandler_dev.cc, Implement /dev.
+
+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 <sys/statvfs.h>
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "devices.h"
+
+#define _LIBC
+#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)
+{
+}
+
+int
+fhandler_dev::open (int flags, mode_t mode)
+{
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ {
+ set_errno (EEXIST);
+ return 0;
+ }
+ if (flags & O_WRONLY)
+ {
+ set_errno (EISDIR);
+ return 0;
+ }
+ /* Filter O_CREAT flag to avoid creating a file called /dev accidentally. */
+ int ret = fhandler_disk_file::open (flags & ~O_CREAT, mode);
+ if (!ret)
+ {
+ /* Open a fake handle to \\Device\\Null */
+ ret = open_null (flags);
+ dir_exists = false;
+ }
+ return ret;
+}
+
+int
+fhandler_dev::close ()
+{
+ return fhandler_disk_file::close ();
+}
+
+int
+fhandler_dev::fstat (struct stat *st)
+{
+ /* If /dev really exists on disk, return correct disk information. */
+ if (pc.fs_got_fs ())
+ return fhandler_disk_file::fstat (st);
+ /* Otherwise fake virtual filesystem. */
+ fhandler_base::fstat (st);
+ st->st_ino = 2;
+ st->st_mode = S_IFDIR | STD_RBITS | STD_XBITS;
+ st->st_nlink = 1;
+ return 0;
+}
+
+int
+fhandler_dev::fstatvfs (struct statvfs *sfs)
+{
+ int ret = -1, opened = 0;
+ HANDLE fh = get_handle ();
+
+ if (!fh)
+ {
+ if (!open (O_RDONLY, 0))
+ return -1;
+ opened = 1;
+ }
+ if (pc.fs_got_fs ())
+ ret = fhandler_disk_file::fstatvfs (sfs);
+ else
+ {
+ /* Virtual file system. Just return an empty buffer with a few values
+ set to something useful similar to Linux. */
+ memset (sfs, 0, sizeof (*sfs));
+ sfs->f_bsize = sfs->f_frsize = 4096;
+ sfs->f_flag = ST_RDONLY;
+ sfs->f_namemax = NAME_MAX;
+ ret = 0;
+ }
+ if (opened)
+ close ();
+ return ret;
+}
+
+int
+fhandler_dev::rmdir ()
+{
+ set_errno (ENOTEMPTY);
+ return -1;
+}
+
+DIR *
+fhandler_dev::opendir (int fd)
+{
+ DIR *dir = fhandler_disk_file::opendir (fd);
+ if (dir)
+ dir_exists = true;
+ else 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 if (!open (O_RDONLY, 0))
+ goto free_dirent;
+ else
+ {
+ cfd = this;
+ dir->__d_fd = cfd;
+ }
+ set_close_on_exec (true);
+ dir->__fh = this;
+ dir_exists = false;
+ drive = part = 0;
+ }
+
+ devidx = dir_exists ? NULL : dev_storage_scan_start;
+
+ syscall_printf ("%p = opendir (%s)", dir, get_name ());
+ return dir;
+
+free_dirent:
+ free (dir->__d_dirent);
+free_dir:
+ free (dir);
+ return NULL;
+}
+
+static const WCHAR *hd_pattern = L"\\Device\\Harddisk%u\\Partition%u";
+
+int
+fhandler_dev::readdir (DIR *dir, dirent *de)
+{
+ int ret;
+ const _device *curdev;
+ 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)
+ goto out;
+ devidx = dev_storage_scan_start;
+ }
+
+ /* Now start processing our internal dev table. */
+ ret = ENMFILE;
+ while ((curdev = devidx++) < dev_storage_end)
+ {
+ /* If exists returns < 0 it means that the device can be used by a
+ program but its use is deprecated and, so, it is not returned
+ by readdir((). */
+ device *cdev = (device *) curdev;
+ if (cdev->exists () <= 0)
+ continue;
+ ++dir->__d_position;
+ strcpy (de->d_name, cdev->name () + dev_prefix_len);
+ if (cdev->get_major () == DEV_TTY_MAJOR
+ && (cdev->is_device (FH_CONIN)
+ || cdev->is_device (FH_CONOUT)
+ || cdev->is_device (FH_CONSOLE)))
+ {
+ /* Make sure conin, conout, and console have the same inode number
+ as the current consX. */
+ de->d_ino = myself->ctty;
+ }
+ else
+ de->d_ino = cdev->get_device ();
+ de->d_type = cdev->type ();
+ ret = 0;
+ break;
+ }
+ /* Last but not least, scan for existing disks/partitions. */
+ if (ret)
+ {
+ UNICODE_STRING upath;
+ WCHAR buf[(sizeof *hd_pattern + 32) / sizeof (wchar_t)];
+ OBJECT_ATTRIBUTES attr;
+ FILE_BASIC_INFORMATION fbi;
+ NTSTATUS status;
+
+ InitializeObjectAttributes (&attr, &upath, 0, NULL, NULL);
+ while (drive < 128)
+ {
+ while (part < 64)
+ {
+ USHORT len = __small_swprintf (buf, hd_pattern, drive, part);
+ RtlInitCountedUnicodeString (&upath, buf, len * sizeof (WCHAR));
+ status = NtQueryAttributesFile (&attr, &fbi);
+ debug_printf ("%S %y", &upath, status);
+ if (status != STATUS_OBJECT_NAME_NOT_FOUND
+ && status != STATUS_OBJECT_PATH_NOT_FOUND)
+ {
+ device dev (drive, part);
+ strcpy (de->d_name, dev.name () + 5);
+ de->d_ino = dev.get_device ();
+ de->d_type = DT_BLK;
+ ++part;
+ ret = 0;
+ goto out;
+ }
+ if (part == 0)
+ break;
+ ++part;
+ }
+ part = 0;
+ ++drive;
+ }
+ }
+
+out:
+ debug_printf ("returning %d", ret);
+ return ret;
+}
+
+void
+fhandler_dev::rewinddir (DIR *dir)
+{
+ devidx = dir_exists ? NULL : dev_storage_scan_start;
+ dir->__d_position = 0;
+ if (dir_exists)
+ fhandler_disk_file::rewinddir (dir);
+}