This is a really old and crappy API, as the previous commit shows. Use NtQueryInformationFile(FilePositionInformation) here instead. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
		
			
				
	
	
		
			766 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			766 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* fhandler_floppy.cc.  See fhandler.h for a description of the
 | |
|    fhandler classes.
 | |
| 
 | |
| 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 <alloca.h>
 | |
| #include <unistd.h>
 | |
| #include <sys/param.h>
 | |
| #include <winioctl.h>
 | |
| #include <cygwin/rdevio.h>
 | |
| #include <cygwin/hdreg.h>
 | |
| #include <cygwin/fs.h>
 | |
| #include "cygerrno.h"
 | |
| #include "security.h"
 | |
| #include "path.h"
 | |
| #include "fhandler.h"
 | |
| #include "ntdll.h"
 | |
| 
 | |
| #define IS_EOM(err)	((err) == ERROR_INVALID_PARAMETER \
 | |
| 			 || (err) == ERROR_SEEK \
 | |
| 			 || (err) == ERROR_SECTOR_NOT_FOUND)
 | |
| 
 | |
| #define bytes_per_sector devbufalign
 | |
| 
 | |
| /**********************************************************************/
 | |
| /* fhandler_dev_floppy */
 | |
| 
 | |
| fhandler_dev_floppy::fhandler_dev_floppy ()
 | |
|   : fhandler_dev_raw (), status ()
 | |
| {
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_dev_floppy::get_drive_info (struct hd_geometry *geo)
 | |
| {
 | |
|   char dbuf[256];
 | |
|   char pbuf[256];
 | |
| 
 | |
|   DISK_GEOMETRY_EX *dix = NULL;
 | |
|   DISK_GEOMETRY *di = NULL;
 | |
|   PARTITION_INFORMATION_EX *pix = NULL;
 | |
|   PARTITION_INFORMATION *pi = NULL;
 | |
|   DWORD bytes_read = 0;
 | |
| 
 | |
|   /* Always try using the new EX ioctls first.  If not available, fall back
 | |
|      to trying the non-EX ioctls which are unfortunately not implemented in
 | |
|      the floppy driver. */
 | |
|   if (get_major () != DEV_FLOPPY_MAJOR)
 | |
|     {
 | |
|       if (!DeviceIoControl (get_handle (),
 | |
| 			    IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0,
 | |
| 			    dbuf, 256, &bytes_read, NULL))
 | |
| 	__seterrno ();
 | |
|       else
 | |
| 	{
 | |
| 	  dix = (DISK_GEOMETRY_EX *) dbuf;
 | |
| 	  di = &dix->Geometry;
 | |
| 	}
 | |
|     }
 | |
|   if (!di)
 | |
|     {
 | |
|       if (!DeviceIoControl (get_handle (),
 | |
| 			    IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
 | |
| 			    dbuf, 256, &bytes_read, NULL))
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  return -1;
 | |
| 	}
 | |
|       di = (DISK_GEOMETRY *) dbuf;
 | |
|     }
 | |
|   if (dix) /* Don't try IOCTL_DISK_GET_PARTITION_INFO_EX if
 | |
| 	      IOCTL_DISK_GET_DRIVE_GEOMETRY_EX didn't work. 
 | |
| 	      Probably a floppy.*/
 | |
|     {
 | |
|       if (!DeviceIoControl (get_handle (),
 | |
| 			    IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0,
 | |
| 			    pbuf, 256, &bytes_read, NULL))
 | |
| 	__seterrno ();
 | |
|       else
 | |
| 	pix = (PARTITION_INFORMATION_EX *) pbuf;
 | |
|     }
 | |
|   if (!pix && get_major () != DEV_FLOPPY_MAJOR)
 | |
|     {
 | |
|       /* It's unlikely that this code path will be used at all.  Either the
 | |
| 	 _EX call already worked, or it's a floppy.  But it doesn't hurt to
 | |
| 	 keep the code in. */
 | |
|       if (!DeviceIoControl (get_handle (),
 | |
| 			    IOCTL_DISK_GET_PARTITION_INFO, NULL, 0,
 | |
| 			    pbuf, 256, &bytes_read, NULL))
 | |
| 	__seterrno ();
 | |
|       else
 | |
| 	pi = (PARTITION_INFORMATION *) pbuf;
 | |
|     }
 | |
|   debug_printf ("disk geometry: (%D cyl)*(%u trk)*(%u sec)*(%u bps)",
 | |
| 		 di->Cylinders.QuadPart,
 | |
| 		 di->TracksPerCylinder,
 | |
| 		 di->SectorsPerTrack,
 | |
| 		 di->BytesPerSector);
 | |
|   bytes_per_sector = di->BytesPerSector;
 | |
|   if (pix)
 | |
|     {
 | |
|       debug_printf ("partition info: offset %D  length %D",
 | |
| 		    pix->StartingOffset.QuadPart,
 | |
| 		    pix->PartitionLength.QuadPart);
 | |
|       drive_size = pix->PartitionLength.QuadPart;
 | |
|     }
 | |
|   else if (pi)
 | |
|     {
 | |
|       debug_printf ("partition info: offset %D  length %D",
 | |
| 		    pi->StartingOffset.QuadPart,
 | |
| 		    pi->PartitionLength.QuadPart);
 | |
|       drive_size = pi->PartitionLength.QuadPart;
 | |
|     }
 | |
|   else	/* Floppy drive. */
 | |
|     drive_size = di->Cylinders.QuadPart * di->TracksPerCylinder
 | |
| 		 * di->SectorsPerTrack * di->BytesPerSector;
 | |
|   if (geo)
 | |
|     {
 | |
|       geo->heads = di->TracksPerCylinder;
 | |
|       geo->sectors = di->SectorsPerTrack;
 | |
|       geo->cylinders = di->Cylinders.LowPart;
 | |
|       if (pix)
 | |
| 	geo->start = pix->StartingOffset.QuadPart >> 9ULL;
 | |
|       else if (pi)
 | |
| 	geo->start = pi->StartingOffset.QuadPart >> 9ULL;
 | |
|       else
 | |
| 	geo->start = 0;
 | |
|     }
 | |
|   debug_printf ("drive size: %D", drive_size);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* Wrapper functions for ReadFile and WriteFile to simplify error handling. */
 | |
| BOOL
 | |
| fhandler_dev_floppy::read_file (void *buf, DWORD to_read, DWORD *read, int *err)
 | |
| {
 | |
|   BOOL ret;
 | |
| 
 | |
|   *err = 0;
 | |
|   if (!(ret = ReadFile (get_handle (), buf, to_read, read, 0)))
 | |
|     *err = GetLastError ();
 | |
|   syscall_printf ("%d (err %d) = ReadFile (%p, %p, to_read %u, read %u, 0)",
 | |
| 		  ret, *err, get_handle (), buf, to_read, *read);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| /* See comment in write_file below. */
 | |
| BOOL
 | |
| fhandler_dev_floppy::lock_partition (DWORD to_write)
 | |
| {
 | |
|   DWORD bytes_read;
 | |
| 
 | |
|   /* The simple case.  We have only a single partition open anyway.
 | |
|      Try to lock the partition so that a subsequent write succeeds.
 | |
|      If there's some file handle open on one of the affected partitions,
 | |
|      this fails, but that's how it works...
 | |
|      The high partition major numbers don't have a partition 0. */
 | |
|   if (get_major () >= DEV_SD_HIGHPART_START || get_minor () % 16 != 0)
 | |
|     {
 | |
|       if (!DeviceIoControl (get_handle (), FSCTL_LOCK_VOLUME,
 | |
| 			   NULL, 0, NULL, 0, &bytes_read, NULL))
 | |
| 	{
 | |
| 	  debug_printf ("DeviceIoControl (FSCTL_LOCK_VOLUME) failed, %E");
 | |
| 	  return FALSE;
 | |
| 	}
 | |
|       return TRUE;
 | |
|     }
 | |
| 
 | |
|   /* The tricky case.  We're writing to the entire disk.  What this code
 | |
|      basically does is to find out if the current write operation affects
 | |
|      one or more partitions on the disk.  If so, it tries to lock all these
 | |
|      partitions and stores the handles for a subsequent close(). */
 | |
|   NTSTATUS status;
 | |
|   IO_STATUS_BLOCK io;
 | |
|   FILE_POSITION_INFORMATION fpi;
 | |
|   /* Allocate space for 4 times the maximum partition count we can handle.
 | |
|      The reason is that for *every* single logical drive in an extended
 | |
|      partition on an MBR drive, 3 filler entries with partition number set
 | |
|      to 0 are added into the partition table returned by
 | |
|      IOCTL_DISK_GET_DRIVE_LAYOUT_EX.  The first of them reproduces the data
 | |
|      of the next partition entry, if any, except for the partiton number.
 | |
|      Then two entries with everything set to 0 follow.  Well, the
 | |
|      documentation states that for MBR drives the number of partition entries
 | |
|      in the PARTITION_INFORMATION_EX array is always a multiple of 4, but,
 | |
|      nevertheless, how crappy is that layout? */
 | |
|   const DWORD size = sizeof (DRIVE_LAYOUT_INFORMATION_EX)
 | |
| 		     + 4 * MAX_PARTITIONS * sizeof (PARTITION_INFORMATION_EX);
 | |
|   PDRIVE_LAYOUT_INFORMATION_EX pdlix = (PDRIVE_LAYOUT_INFORMATION_EX)
 | |
| 				       alloca (size);
 | |
|   BOOL found = FALSE;
 | |
| 
 | |
|   /* Fetch current file pointer position on disk. */
 | |
|   status = NtQueryInformationFile (get_handle (), &io, &fpi, sizeof fpi,
 | |
| 				   FilePositionInformation);
 | |
|   if (!NT_SUCCESS (status))
 | |
|     {
 | |
|       debug_printf ("NtQueryInformationFile(FilePositionInformation): %y",
 | |
| 		    status);
 | |
|       return FALSE;
 | |
|     }
 | |
|   /* Fetch drive layout to get start and end positions of partitions on disk. */
 | |
|   if (!DeviceIoControl (get_handle (), IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0,
 | |
| 			pdlix, size, &bytes_read, NULL))
 | |
|     {
 | |
|       debug_printf ("DeviceIoControl(IOCTL_DISK_GET_DRIVE_LAYOUT_EX): %E");
 | |
|       return FALSE;
 | |
|     }
 | |
|   /* Scan through partition info to find the partition(s) into which we're
 | |
|      currently trying to write. */
 | |
|   PARTITION_INFORMATION_EX *ppie = pdlix->PartitionEntry;
 | |
|   for (DWORD i = 0; i < pdlix->PartitionCount; ++i, ++ppie)
 | |
|     {
 | |
|       /* A partition number of 0 denotes an extended partition or one of the
 | |
| 	 aforementioned filler entries.  Just skip. */
 | |
|       if (ppie->PartitionNumber == 0)
 | |
| 	continue;
 | |
|       /* Check if our writing range affects this partition. */
 | |
|       if (fpi.CurrentByteOffset.QuadPart   < ppie->StartingOffset.QuadPart
 | |
| 					     + ppie->PartitionLength.QuadPart
 | |
| 	  && ppie->StartingOffset.QuadPart < fpi.CurrentByteOffset.QuadPart
 | |
| 					     + to_write)
 | |
| 	{
 | |
| 	  /* Yes.  Now check if we can handle it.  We can only handle
 | |
| 	     up to MAX_PARTITIONS partitions.  The partition numbering is
 | |
| 	     one-based, so we decrement the partition number by 1 when using
 | |
| 	     as index into the partition array. */
 | |
| 	  DWORD &part_no = ppie->PartitionNumber;
 | |
| 	  if (part_no >= MAX_PARTITIONS)
 | |
| 	    return FALSE;
 | |
| 	  found = TRUE;
 | |
| 	  debug_printf ("%u %D->%D : %D->%D", part_no,
 | |
| 			ppie->StartingOffset.QuadPart,
 | |
| 			ppie->StartingOffset.QuadPart
 | |
| 			+ ppie->PartitionLength.QuadPart,
 | |
| 			fpi.CurrentByteOffset.QuadPart,
 | |
| 			fpi.CurrentByteOffset.QuadPart + to_write);
 | |
| 	  /* Do we already have partitions?  If not, create it. */
 | |
| 	  if (!partitions)
 | |
| 	    {
 | |
| 	      partitions = (part_t *) ccalloc_abort (HEAP_FHANDLER, 1,
 | |
| 						     sizeof (part_t));
 | |
| 	      partitions->refcnt = 1;
 | |
| 	    }
 | |
| 	  /* Next, check if the partition is already open.  If so, skip it. */
 | |
| 	  if (partitions->hdl[part_no - 1])
 | |
| 	    continue;
 | |
| 	  /* Now open the partition and lock it. */
 | |
| 	  WCHAR part[MAX_PATH], *p;
 | |
| 	  NTSTATUS status;
 | |
| 	  UNICODE_STRING upart;
 | |
| 	  OBJECT_ATTRIBUTES attr;
 | |
| 	  IO_STATUS_BLOCK io;
 | |
| 
 | |
| 	  sys_mbstowcs (part, MAX_PATH, get_win32_name ());
 | |
| 	  p = wcschr (part, L'\0') - 1;
 | |
| 	  __small_swprintf (p, L"%d", part_no);
 | |
| 	  RtlInitUnicodeString (&upart, part);
 | |
| 	  InitializeObjectAttributes (&attr, &upart,
 | |
| 				      OBJ_CASE_INSENSITIVE
 | |
| 				      | ((get_flags () & O_CLOEXEC)
 | |
| 					 ? 0 : OBJ_INHERIT),
 | |
| 				      NULL, NULL);
 | |
| 	  status = NtOpenFile (&partitions->hdl[part_no - 1],
 | |
| 			       GENERIC_READ | GENERIC_WRITE, &attr,
 | |
| 			       &io, FILE_SHARE_READ | FILE_SHARE_WRITE, 0);
 | |
| 	  if (!NT_SUCCESS (status))
 | |
| 	    {
 | |
| 	      debug_printf ("NtCreateFile(%W): %y", part, status);
 | |
| 	      return FALSE;
 | |
| 	    }
 | |
| 	  if (!DeviceIoControl (partitions->hdl[part_no - 1], FSCTL_LOCK_VOLUME,
 | |
| 				NULL, 0, NULL, 0, &bytes_read, NULL))
 | |
| 	    {
 | |
| 	      debug_printf ("DeviceIoControl (%W, FSCTL_LOCK_VOLUME) "
 | |
| 			    "failed, %E", part);
 | |
| 	      return FALSE;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|   /* If we didn't find a single matching partition, the "Access denied"
 | |
|      had another reason, so return FALSE in that case. */
 | |
|   return found;
 | |
| }
 | |
| 
 | |
| BOOL
 | |
| fhandler_dev_floppy::write_file (const void *buf, DWORD to_write,
 | |
| 				 DWORD *written, int *err)
 | |
| {
 | |
|   BOOL ret;
 | |
| 
 | |
|   *err = 0;
 | |
|   if (!(ret = WriteFile (get_handle (), buf, to_write, written, 0)))
 | |
|     *err = GetLastError ();
 | |
|   /* When writing to a disk or partition an "Access denied" error may
 | |
|      occur due to the raw disk write restriction.
 | |
|      See http://support.microsoft.com/kb/942448 for details.
 | |
|      What we do here is to lock the affected partition(s) and retry. */
 | |
|   if (*err == ERROR_ACCESS_DENIED
 | |
|       && get_major () != DEV_FLOPPY_MAJOR
 | |
|       && get_major () != DEV_CDROM_MAJOR
 | |
|       && (get_flags () & O_ACCMODE) != O_RDONLY
 | |
|       && lock_partition (to_write))
 | |
|     {
 | |
|       *err = 0;
 | |
|       if (!(ret = WriteFile (get_handle (), buf, to_write, written, 0)))
 | |
| 	*err = GetLastError ();
 | |
|     }
 | |
|   syscall_printf ("%d (err %d) = WriteFile (%p, %p, write %u, written %u, 0)",
 | |
| 		  ret, *err, get_handle (), buf, to_write, *written);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_dev_floppy::open (int flags, mode_t)
 | |
| {
 | |
|   int ret = fhandler_dev_raw::open (flags);
 | |
| 
 | |
|   if (ret)
 | |
|     {
 | |
|       DWORD bytes_read;
 | |
| 
 | |
|       if (get_drive_info (NULL))
 | |
| 	{
 | |
| 	  close ();
 | |
| 	  return 0;
 | |
| 	}
 | |
|       if (!(flags & O_DIRECT))
 | |
| 	{
 | |
| 	  /* Create sector-aligned buffer.  As default buffer size, we're using
 | |
| 	     some big, sector-aligned value.  Since direct blockdev IO is
 | |
| 	     usually non-buffered and non-cached, the performance without
 | |
| 	     buffering is worse than access to a file system on same device.
 | |
| 	     Whoever uses O_DIRECT has my condolences. */
 | |
| 	  devbufsiz = MAX (16 * bytes_per_sector, 65536);
 | |
| 	  devbufalloc = new char [devbufsiz + devbufalign];
 | |
| 	  devbuf = (char *) roundup2 ((uintptr_t) devbufalloc,
 | |
| 				      (uintptr_t) devbufalign);
 | |
| 	}
 | |
| 
 | |
|       /* If we're not trying to access a floppy disk, make sure we're actually
 | |
|          allowed to read *all* of the device or volume.  This is actually
 | |
| 	 documented in the MSDN CreateFile man page. */
 | |
|       if (get_major () != DEV_FLOPPY_MAJOR
 | |
| 	  && !DeviceIoControl (get_handle (), FSCTL_ALLOW_EXTENDED_DASD_IO,
 | |
| 			       NULL, 0, NULL, 0, &bytes_read, NULL))
 | |
| 	debug_printf ("DeviceIoControl (FSCTL_ALLOW_EXTENDED_DASD_IO) "
 | |
| 		      "failed, %E");
 | |
|     }
 | |
| 
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_dev_floppy::close ()
 | |
| {
 | |
|   int ret = fhandler_dev_raw::close ();
 | |
| 
 | |
|   if (partitions && InterlockedDecrement (&partitions->refcnt) == 0)
 | |
|     {
 | |
|       for (int i = 0; i < MAX_PARTITIONS; ++i)
 | |
| 	if (partitions->hdl[i])
 | |
| 	  NtClose (partitions->hdl[i]);
 | |
|       cfree (partitions);
 | |
|     }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_dev_floppy::dup (fhandler_base *child, int flags)
 | |
| {
 | |
|   int ret = fhandler_dev_raw::dup (child, flags);
 | |
| 
 | |
|   if (!ret && partitions)
 | |
|     InterlockedIncrement (&partitions->refcnt);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| inline off_t
 | |
| fhandler_dev_floppy::get_current_position ()
 | |
| {
 | |
|   IO_STATUS_BLOCK io;
 | |
|   FILE_POSITION_INFORMATION fpi = { { QuadPart : 0LL } };
 | |
| 
 | |
|   NtQueryInformationFile (get_handle (), &io, &fpi, sizeof fpi,
 | |
| 			  FilePositionInformation);
 | |
|   return fpi.CurrentByteOffset.QuadPart;
 | |
| }
 | |
| 
 | |
| void __reg3
 | |
| fhandler_dev_floppy::raw_read (void *ptr, size_t& ulen)
 | |
| {
 | |
|   DWORD bytes_read = 0;
 | |
|   DWORD read2;
 | |
|   DWORD bytes_to_read;
 | |
|   int ret;
 | |
|   size_t len = ulen;
 | |
|   char *tgt;
 | |
|   char *p = (char *) ptr;
 | |
| 
 | |
|   /* Checking a previous end of media */
 | |
|   if (eom_detected () && !lastblk_to_read ())
 | |
|     {
 | |
|       set_errno (ENOSPC);
 | |
|       goto err;
 | |
|     }
 | |
| 
 | |
|   if (devbuf)
 | |
|     {
 | |
|       while (len > 0)
 | |
| 	{
 | |
| 	  if (devbufstart < devbufend)
 | |
| 	    {
 | |
| 	      bytes_to_read = MIN (len, devbufend - devbufstart);
 | |
| 	      debug_printf ("read %u bytes from buffer (rest %u)",
 | |
| 			    bytes_to_read,
 | |
| 			    devbufend - devbufstart - bytes_to_read);
 | |
| 	      memcpy (p, devbuf + devbufstart, bytes_to_read);
 | |
| 	      len -= bytes_to_read;
 | |
| 	      p += bytes_to_read;
 | |
| 	      bytes_read += bytes_to_read;
 | |
| 	      devbufstart += bytes_to_read;
 | |
| 
 | |
| 	      if (lastblk_to_read ())
 | |
| 		{
 | |
| 		  lastblk_to_read (false);
 | |
| 		  break;
 | |
| 		}
 | |
| 	    }
 | |
| 	  if (len > 0)
 | |
| 	    {
 | |
| 	      if (len >= devbufsiz)
 | |
| 		{
 | |
| 		  bytes_to_read = (len / bytes_per_sector) * bytes_per_sector;
 | |
| 		  tgt = p;
 | |
| 		}
 | |
| 	      else
 | |
| 		{
 | |
| 		  tgt = devbuf;
 | |
| 		  bytes_to_read = devbufsiz;
 | |
| 		}
 | |
| 	      off_t current_position = get_current_position ();
 | |
| 	      if (current_position + bytes_to_read >= drive_size)
 | |
| 		bytes_to_read = drive_size - current_position;
 | |
| 	      if (!bytes_to_read)
 | |
| 		break;
 | |
| 
 | |
| 	      debug_printf ("read %u bytes from pos %U %s", bytes_to_read,
 | |
| 			    current_position,
 | |
| 			    len < devbufsiz ? "into buffer" : "directly");
 | |
| 	      if (!read_file (tgt, bytes_to_read, &read2, &ret))
 | |
| 		{
 | |
| 		  if (!IS_EOM (ret))
 | |
| 		    {
 | |
| 		      __seterrno ();
 | |
| 		      goto err;
 | |
| 		    }
 | |
| 
 | |
| 		  eom_detected (true);
 | |
| 
 | |
| 		  if (!read2)
 | |
| 		    {
 | |
| 		      if (!bytes_read)
 | |
| 			{
 | |
| 			  debug_printf ("return -1, set errno to ENOSPC");
 | |
| 			  set_errno (ENOSPC);
 | |
| 			  goto err;
 | |
| 			}
 | |
| 		      break;
 | |
| 		    }
 | |
| 		  lastblk_to_read (true);
 | |
| 		}
 | |
| 	      if (!read2)
 | |
| 	       break;
 | |
| 	      if (tgt == devbuf)
 | |
| 		{
 | |
| 		  devbufstart = 0;
 | |
| 		  devbufend = read2;
 | |
| 		}
 | |
| 	      else
 | |
| 		{
 | |
| 		  len -= read2;
 | |
| 		  p += read2;
 | |
| 		  bytes_read += read2;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       off_t current_position = get_current_position ();
 | |
|       bytes_to_read = len;
 | |
|       if (current_position + bytes_to_read >= drive_size)
 | |
| 	bytes_to_read = drive_size - current_position;
 | |
|       debug_printf ("read %u bytes from pos %U directly", bytes_to_read,
 | |
| 		    current_position);
 | |
|       if (bytes_to_read && !read_file (p, bytes_to_read, &bytes_read, &ret))
 | |
| 	{
 | |
| 	  if (!IS_EOM (ret))
 | |
| 	    {
 | |
| 	      __seterrno ();
 | |
| 	      goto err;
 | |
| 	    }
 | |
| 	  if (bytes_read)
 | |
| 	    eom_detected (true);
 | |
| 	  else
 | |
| 	    {
 | |
| 	      debug_printf ("return -1, set errno to ENOSPC");
 | |
| 	      set_errno (ENOSPC);
 | |
| 	      goto err;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   ulen = (size_t) bytes_read;
 | |
|   return;
 | |
| 
 | |
| err:
 | |
|   ulen = (size_t) -1;
 | |
| }
 | |
| 
 | |
| ssize_t __reg3
 | |
| fhandler_dev_floppy::raw_write (const void *ptr, size_t len)
 | |
| {
 | |
|   DWORD bytes_written = 0;
 | |
|   char *p = (char *) ptr;
 | |
|   int ret;
 | |
| 
 | |
|   /* Checking a previous end of media */
 | |
|   if (eom_detected ())
 | |
|     {
 | |
|       set_errno (ENOSPC);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (!len)
 | |
|     return 0;
 | |
| 
 | |
|   if (devbuf)
 | |
|     {
 | |
|       DWORD cplen, written;
 | |
| 
 | |
|       /* First check if we have an active read buffer.  If so, try to fit in
 | |
|       	 the start of the input buffer and write out the entire result.
 | |
| 	 This also covers the situation after lseek since lseek fills the read
 | |
| 	 buffer in case we seek to an address which is not sector aligned. */
 | |
|       if (devbufend && devbufstart < devbufend)
 | |
|       	{
 | |
| 	  off_t current_pos = get_current_position ();
 | |
| 	  cplen = MIN (len, devbufend - devbufstart);
 | |
| 	  memcpy (devbuf + devbufstart, p, cplen);
 | |
| 	  LARGE_INTEGER off = { QuadPart:current_pos - devbufend };
 | |
| 	  if (!SetFilePointerEx (get_handle (), off, NULL, FILE_BEGIN))
 | |
| 	    {
 | |
| 	      devbufstart = devbufend = 0;
 | |
| 	      __seterrno ();
 | |
| 	      return -1;
 | |
| 	    }
 | |
| 	  if (!write_file (devbuf, devbufend, &written, &ret))
 | |
| 	    {
 | |
| 	      devbufstart = devbufend = 0;
 | |
| 	      goto err;
 | |
| 	    }
 | |
| 	  /* Align pointers, lengths, etc. */
 | |
| 	  cplen = MIN (cplen, written);
 | |
| 	  devbufstart += cplen;
 | |
| 	  if (devbufstart >= devbufend)
 | |
| 	    devbufstart = devbufend = 0;
 | |
| 	  p += cplen;
 | |
| 	  len -= cplen;
 | |
| 	  bytes_written += cplen;
 | |
| 	}
 | |
|       /* As long as there's still something left in the input buffer ... */
 | |
|       while (len)
 | |
| 	{
 | |
| 	  /* Compute the length to write.  The problem is that the underlying
 | |
| 	     driver may require sector aligned read/write.  So we copy the data
 | |
| 	     over to devbuf, which is guaranteed to be sector aligned. */
 | |
| 	  cplen = MIN (len, devbufsiz);
 | |
| 	  if (cplen >= bytes_per_sector)
 | |
| 	    /* If the remaining len is >= sector size, write out the maximum
 | |
| 	       possible multiple of the sector size which fits into devbuf. */
 | |
| 	    cplen = rounddown (cplen, bytes_per_sector);
 | |
| 	  else
 | |
| 	    {
 | |
| 	      /* If len < sector size, read in the next sector, seek back,
 | |
| 		 and just copy the new data over the old one before writing. */
 | |
| 	      LARGE_INTEGER off = { QuadPart:get_current_position () };
 | |
| 	      if (!read_file (devbuf, bytes_per_sector, &written, &ret))
 | |
| 		goto err;
 | |
| 	      if (!SetFilePointerEx (get_handle (), off, NULL, FILE_BEGIN))
 | |
| 		{
 | |
| 		  __seterrno ();
 | |
| 		  return -1;
 | |
| 		}
 | |
| 	    }
 | |
| 	  memcpy (devbuf, p, cplen);
 | |
| 	  if (!write_file (devbuf, MAX (cplen, bytes_per_sector), &written,
 | |
| 			   &ret))
 | |
| 	    {
 | |
| 	      bytes_written += MIN (cplen, written);
 | |
| 	      goto err;
 | |
| 	    }
 | |
| 	  cplen = MIN (cplen, written);
 | |
| 	  p += cplen;
 | |
| 	  len -= cplen;
 | |
| 	  bytes_written += cplen;
 | |
| 	  /* If we overwrote, revalidate devbuf.  It still contains the
 | |
| 	     content from the above read/modify/write.  Revalidating makes
 | |
| 	     sure lseek reports the correct position. */
 | |
| 	  if (cplen < bytes_per_sector)
 | |
| 	    {
 | |
| 	      devbufstart = cplen;
 | |
| 	      devbufend = bytes_per_sector;
 | |
| 	    }
 | |
| 	}
 | |
|       return bytes_written;
 | |
|     }
 | |
|   
 | |
|   /* In O_DIRECT case, just write. */
 | |
|   if (write_file (p, len, &bytes_written, &ret))
 | |
|     return bytes_written;
 | |
| 
 | |
| err:
 | |
|   if (IS_EOM (ret))
 | |
|     {
 | |
|       eom_detected (true);
 | |
|       if (!bytes_written)
 | |
| 	set_errno (ENOSPC);
 | |
|     }
 | |
|   else if (!bytes_written)
 | |
|     __seterrno ();
 | |
|   return bytes_written ?: -1;
 | |
| }
 | |
| 
 | |
| off_t
 | |
| fhandler_dev_floppy::lseek (off_t offset, int whence)
 | |
| {
 | |
|   char buf[bytes_per_sector];
 | |
|   off_t current_pos = (off_t) -1;
 | |
|   LARGE_INTEGER sector_aligned_offset;
 | |
|   size_t bytes_left;
 | |
| 
 | |
|   if (whence == SEEK_END)
 | |
|     {
 | |
|       offset += drive_size;
 | |
|       whence = SEEK_SET;
 | |
|     }
 | |
|   else if (whence == SEEK_CUR)
 | |
|     {
 | |
|       current_pos = get_current_position ();
 | |
|       off_t exact_pos = current_pos - (devbufend - devbufstart);
 | |
|       /* Shortcut when used to get current position. */
 | |
|       if (offset == 0)
 | |
|       	return exact_pos;
 | |
|       offset += exact_pos;
 | |
|       whence = SEEK_SET;
 | |
|     }
 | |
| 
 | |
|   if (whence != SEEK_SET || offset < 0 || offset > drive_size)
 | |
|     {
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   /* If new position is in buffered range, adjust buffer and return */
 | |
|   if (devbufstart < devbufend)
 | |
|     {
 | |
|       if (current_pos == (off_t) -1)
 | |
| 	current_pos = get_current_position ();
 | |
|       if (current_pos - devbufend <= offset && offset <= current_pos)
 | |
| 	{
 | |
| 	  devbufstart = devbufend - (current_pos - offset);
 | |
| 	  return offset;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   sector_aligned_offset.QuadPart = rounddown (offset, bytes_per_sector);
 | |
|   bytes_left = offset - sector_aligned_offset.QuadPart;
 | |
| 
 | |
|   /* Invalidate buffer. */
 | |
|   devbufstart = devbufend = 0;
 | |
| 
 | |
|   if (!SetFilePointerEx (get_handle (), sector_aligned_offset, NULL,
 | |
| 			 FILE_BEGIN))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   eom_detected (false);
 | |
| 
 | |
|   if (bytes_left)
 | |
|     {
 | |
|       raw_read (buf, bytes_left);
 | |
|       if (bytes_left == (size_t) -1)
 | |
| 	return -1;
 | |
|     }
 | |
| 
 | |
|   return sector_aligned_offset.QuadPart + bytes_left;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_dev_floppy::ioctl (unsigned int cmd, void *buf)
 | |
| {
 | |
|   int ret = 0;
 | |
|   DWORD bytes_read;
 | |
| 
 | |
|   switch (cmd)
 | |
|     {
 | |
|     case HDIO_GETGEO:
 | |
|       debug_printf ("HDIO_GETGEO");
 | |
|       ret = get_drive_info ((struct hd_geometry *) buf);
 | |
|       break;
 | |
|     case BLKGETSIZE:
 | |
|     case BLKGETSIZE64:
 | |
|       debug_printf ("BLKGETSIZE");
 | |
|       if (cmd == BLKGETSIZE)
 | |
| 	*(long *)buf = drive_size >> 9UL;
 | |
|       else
 | |
| 	*(off_t *)buf = drive_size;
 | |
|       break;
 | |
|     case BLKRRPART:
 | |
|       debug_printf ("BLKRRPART");
 | |
|       if (!DeviceIoControl (get_handle (), IOCTL_DISK_UPDATE_PROPERTIES,
 | |
| 			    NULL, 0, NULL, 0, &bytes_read, NULL))
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  ret = -1;
 | |
| 	}
 | |
|       else
 | |
| 	get_drive_info (NULL);
 | |
|       break;
 | |
|     case BLKSSZGET:
 | |
|       debug_printf ("BLKSSZGET");
 | |
|       *(int *)buf = (int) bytes_per_sector;
 | |
|       break;
 | |
|     case BLKIOMIN:
 | |
|       debug_printf ("BLKIOMIN");
 | |
|       *(int *)buf = (int) bytes_per_sector;
 | |
|       break;
 | |
|     case BLKIOOPT:
 | |
|       debug_printf ("BLKIOOPT");
 | |
|       *(int *)buf = (int) bytes_per_sector;
 | |
|       break;
 | |
|     case BLKPBSZGET:
 | |
|       debug_printf ("BLKPBSZGET");
 | |
|       *(int *)buf = (int) bytes_per_sector;
 | |
|       break;
 | |
|     case BLKALIGNOFF:
 | |
|       debug_printf ("BLKALIGNOFF");
 | |
|       *(int *)buf = 0;
 | |
|       break;
 | |
|     default:
 | |
|       ret = fhandler_dev_raw::ioctl (cmd, buf);
 | |
|       break;
 | |
|     }
 | |
|   return ret;
 | |
| }
 | |
| 
 |