230 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* fhandler_dev.cc, Implement /dev.
 | |
| 
 | |
|    Copyright 2012, 2013 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 <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;
 | |
|     }
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| 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(().  */
 | |
|       if (curdev->exists () <= 0)
 | |
| 	continue;
 | |
|       ++dir->__d_position;
 | |
|       strcpy (de->d_name, curdev->name + dev_prefix_len);
 | |
|       if (curdev->get_major () == DEV_TTY_MAJOR
 | |
| 	  && (curdev->is_device (FH_CONIN)
 | |
| 	      || curdev->is_device (FH_CONOUT)
 | |
| 	      || curdev->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 = curdev->get_device ();
 | |
|       de->d_type = curdev->type ();
 | |
|       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;
 | |
|   dir->__d_position = 0;
 | |
|   if (dir_exists)
 | |
|     fhandler_disk_file::rewinddir (dir);
 | |
| }
 |