| /* 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 _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) |
| { |
| } |
| |
| 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 __reg2 |
| 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 __reg2 |
| 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; |
| } |
| |
| 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); |
| } |