7cdd029300
variable block size, read only one block, read directly into user supplied buffer, return ENOMEM if user supplied buffer is smaller than size of next block to read. Use read2 instead of bytes_to_read to count number of bytes read. * fhandler_tape.cc (fhandler_dev_tape::open): Add debug output.
603 lines
12 KiB
C++
603 lines
12 KiB
C++
/* fhandler_raw.cc. See fhandler.h for a description of the fhandler classes.
|
|
|
|
Copyright 1999, 2000, 2001, 2002, 2003, 2004 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 <sys/termios.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cygwin/rdevio.h>
|
|
#include <sys/mtio.h>
|
|
#include <ntdef.h>
|
|
#include "cygerrno.h"
|
|
#include "perprocess.h"
|
|
#include "security.h"
|
|
#include "path.h"
|
|
#include "fhandler.h"
|
|
#include "dtable.h"
|
|
#include "cygheap.h"
|
|
#include "ntdll.h"
|
|
|
|
/* static wrapper functions to hide the effect of media changes and
|
|
bus resets which occurs after a new media is inserted. This is
|
|
also related to the used tape device. */
|
|
|
|
static BOOL write_file (HANDLE fh, const void *buf, DWORD to_write,
|
|
DWORD *written, int *err)
|
|
{
|
|
BOOL ret;
|
|
|
|
*err = 0;
|
|
if (!(ret = WriteFile (fh, buf, to_write, written, 0)))
|
|
{
|
|
if ((*err = GetLastError ()) == ERROR_MEDIA_CHANGED
|
|
|| *err == ERROR_BUS_RESET)
|
|
{
|
|
*err = 0;
|
|
if (!(ret = WriteFile (fh, buf, to_write, written, 0)))
|
|
*err = GetLastError ();
|
|
}
|
|
}
|
|
syscall_printf ("%d (err %d) = WriteFile (%d, %d, write %d, written %d, 0)",
|
|
ret, *err, fh, buf, to_write, *written);
|
|
return ret;
|
|
}
|
|
|
|
static BOOL read_file (HANDLE fh, void *buf, DWORD to_read,
|
|
DWORD *read, int *err)
|
|
{
|
|
BOOL ret;
|
|
|
|
*err = 0;
|
|
if (!(ret = ReadFile (fh, buf, to_read, read, 0)))
|
|
{
|
|
if ((*err = GetLastError ()) == ERROR_MEDIA_CHANGED
|
|
|| *err == ERROR_BUS_RESET)
|
|
{
|
|
*err = 0;
|
|
if (!(ret = ReadFile (fh, buf, to_read, read, 0)))
|
|
*err = GetLastError ();
|
|
}
|
|
}
|
|
syscall_printf ("%d (err %d) = ReadFile (%d, %d, to_read %d, read %d, 0)",
|
|
ret, *err, fh, buf, to_read, *read);
|
|
return ret;
|
|
}
|
|
|
|
/**********************************************************************/
|
|
/* fhandler_dev_raw */
|
|
|
|
void
|
|
fhandler_dev_raw::clear (void)
|
|
{
|
|
devbuf = NULL;
|
|
devbufsiz = 0;
|
|
devbufstart = 0;
|
|
devbufend = 0;
|
|
eom_detected = 0;
|
|
eof_detected = 0;
|
|
lastblk_to_read = 0;
|
|
varblkop = 0;
|
|
}
|
|
|
|
int
|
|
fhandler_dev_raw::writebuf (void)
|
|
{
|
|
DWORD written;
|
|
int ret = 0;
|
|
|
|
if (is_writing && devbuf && devbufend)
|
|
{
|
|
DWORD to_write;
|
|
int ret = 0;
|
|
|
|
memset (devbuf + devbufend, 0, devbufsiz - devbufend);
|
|
if (get_major () != DEV_TAPE_MAJOR)
|
|
to_write = ((devbufend - 1) / 512 + 1) * 512;
|
|
else if (varblkop)
|
|
to_write = devbufend;
|
|
else
|
|
to_write = devbufsiz;
|
|
if (!write_file (get_handle (), devbuf, to_write, &written, &ret)
|
|
&& is_eom (ret))
|
|
eom_detected = 1;
|
|
if (written)
|
|
has_written = 1;
|
|
devbufstart = devbufend = 0;
|
|
}
|
|
is_writing = 0;
|
|
return ret;
|
|
}
|
|
|
|
fhandler_dev_raw::fhandler_dev_raw ()
|
|
: fhandler_base ()
|
|
{
|
|
clear ();
|
|
set_need_fork_fixup ();
|
|
}
|
|
|
|
fhandler_dev_raw::~fhandler_dev_raw (void)
|
|
{
|
|
if (devbufsiz > 1L)
|
|
delete [] devbuf;
|
|
clear ();
|
|
}
|
|
|
|
int __stdcall
|
|
fhandler_dev_raw::fstat (struct __stat64 *buf)
|
|
{
|
|
debug_printf ("here");
|
|
|
|
if (get_major () == DEV_TAPE_MAJOR)
|
|
buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
|
|
else
|
|
buf->st_mode = S_IFBLK | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
|
|
|
|
buf->st_uid = geteuid32 ();
|
|
buf->st_gid = getegid32 ();
|
|
buf->st_nlink = 1;
|
|
buf->st_blksize = S_BLKSIZE;
|
|
time_as_timestruc_t (&buf->st_ctim);
|
|
buf->st_atim = buf->st_mtim = buf->st_ctim;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fhandler_dev_raw::open (int flags, mode_t)
|
|
{
|
|
if (!wincap.has_raw_devices ())
|
|
{
|
|
set_errno (ENOENT);
|
|
debug_printf ("%s is accessible under NT/W2K only", get_win32_name ());
|
|
return 0;
|
|
}
|
|
|
|
/* Check for illegal flags. */
|
|
if (flags & (O_APPEND | O_EXCL))
|
|
{
|
|
set_errno (EINVAL);
|
|
return 0;
|
|
}
|
|
|
|
/* Always open a raw device existing and binary. */
|
|
flags &= ~(O_CREAT | O_TRUNC);
|
|
flags |= O_BINARY;
|
|
|
|
DWORD access = GENERIC_READ | SYNCHRONIZE;
|
|
if (get_major () == DEV_TAPE_MAJOR
|
|
|| (flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_WRONLY
|
|
|| (flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDWR)
|
|
access |= GENERIC_WRITE;
|
|
|
|
extern void str2buf2uni (UNICODE_STRING &, WCHAR *, const char *);
|
|
UNICODE_STRING dev;
|
|
WCHAR devname[CYG_MAX_PATH + 1];
|
|
str2buf2uni (dev, devname, get_win32_name ());
|
|
OBJECT_ATTRIBUTES attr;
|
|
InitializeObjectAttributes (&attr, &dev, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
HANDLE h;
|
|
IO_STATUS_BLOCK io;
|
|
NTSTATUS status = NtOpenFile (&h, access, &attr, &io, wincap.shared (),
|
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
__seterrno_from_win_error (RtlNtStatusToDosError (status));
|
|
return 0;
|
|
}
|
|
|
|
set_io_handle (h);
|
|
set_flags ((flags & ~O_TEXT) | O_BINARY);
|
|
|
|
if (devbufsiz > 1L)
|
|
devbuf = new char [devbufsiz];
|
|
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
fhandler_dev_raw::close (void)
|
|
{
|
|
return fhandler_base::close ();
|
|
}
|
|
|
|
void
|
|
fhandler_dev_raw::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;
|
|
|
|
/* In mode O_RDWR the buffer has to be written to device first */
|
|
ret = writebuf ();
|
|
if (ret)
|
|
{
|
|
if (is_eom (ret))
|
|
set_errno (ENOSPC);
|
|
else
|
|
__seterrno ();
|
|
goto err;
|
|
}
|
|
|
|
/* Checking a previous end of file */
|
|
if (eof_detected && !lastblk_to_read)
|
|
{
|
|
eof_detected = 0;
|
|
ulen = 0;
|
|
return;
|
|
}
|
|
|
|
/* 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 %d bytes from buffer (rest %d)",
|
|
bytes_to_read, devbufstart - devbufend);
|
|
memcpy (ptr, devbuf + devbufstart, bytes_to_read);
|
|
len -= bytes_to_read;
|
|
ptr = (void *) ((char *) ptr + bytes_to_read);
|
|
bytes_read += bytes_to_read;
|
|
devbufstart += bytes_to_read;
|
|
|
|
if (lastblk_to_read)
|
|
{
|
|
lastblk_to_read = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (len > 0)
|
|
{
|
|
if (!varblkop && len >= devbufsiz)
|
|
{
|
|
if (get_major () == DEV_TAPE_MAJOR)
|
|
bytes_to_read = (len / devbufsiz) * devbufsiz;
|
|
else
|
|
bytes_to_read = (len / 512) * 512;
|
|
tgt = (char *) ptr;
|
|
debug_printf ("read %d bytes direct from file",bytes_to_read);
|
|
}
|
|
else if (varblkop)
|
|
{
|
|
tgt = (char *) ptr;
|
|
bytes_to_read = len;
|
|
debug_printf ("read variable bytes direct from file");
|
|
}
|
|
else
|
|
{
|
|
tgt = devbuf;
|
|
bytes_to_read = devbufsiz;
|
|
debug_printf ("read %d bytes from file into buffer",
|
|
bytes_to_read);
|
|
}
|
|
if (!read_file (get_handle (), tgt, bytes_to_read, &read2, &ret))
|
|
{
|
|
if (!is_eof (ret) && !is_eom (ret))
|
|
{
|
|
if (varblkop && ret == ERROR_MORE_DATA)
|
|
/* *ulen < blocksize. Linux returns ENOMEM here
|
|
when reading with variable blocksize . */
|
|
set_errno (ENOMEM);
|
|
else
|
|
__seterrno ();
|
|
goto err;
|
|
}
|
|
|
|
if (is_eof (ret))
|
|
eof_detected = 1;
|
|
else
|
|
eom_detected = 1;
|
|
|
|
if (!read2)
|
|
{
|
|
if (!bytes_read && is_eom (ret))
|
|
{
|
|
debug_printf ("return -1, set errno to ENOSPC");
|
|
set_errno (ENOSPC);
|
|
goto err;
|
|
}
|
|
break;
|
|
}
|
|
lastblk_to_read = 1;
|
|
}
|
|
if (!read2)
|
|
break;
|
|
if (tgt == devbuf)
|
|
{
|
|
devbufstart = 0;
|
|
devbufend = read2;
|
|
}
|
|
else if (varblkop)
|
|
{
|
|
/* When reading tapes with variable block size, we
|
|
leave right after reading one block. */
|
|
bytes_read = read2;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
len -= read2;
|
|
ptr = (void *) ((char *) ptr + read2);
|
|
bytes_read += read2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!read_file (get_handle (), ptr, len, &bytes_read, &ret))
|
|
{
|
|
if (!is_eof (ret) && !is_eom (ret))
|
|
{
|
|
__seterrno ();
|
|
goto err;
|
|
}
|
|
if (bytes_read)
|
|
{
|
|
if (is_eof (ret))
|
|
eof_detected = 1;
|
|
else
|
|
eom_detected = 1;
|
|
}
|
|
else if (is_eom (ret))
|
|
{
|
|
debug_printf ("return -1, set errno to ENOSPC");
|
|
set_errno (ENOSPC);
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
ulen = (size_t) bytes_read;
|
|
return;
|
|
|
|
err:
|
|
ulen = (size_t) -1;
|
|
return;
|
|
}
|
|
|
|
int
|
|
fhandler_dev_raw::raw_write (const void *ptr, size_t len)
|
|
{
|
|
DWORD bytes_written = 0;
|
|
DWORD bytes_to_write;
|
|
DWORD written;
|
|
char *p = (char *) ptr;
|
|
char *tgt;
|
|
int ret;
|
|
|
|
/* Checking a previous end of media on tape */
|
|
if (eom_detected)
|
|
{
|
|
set_errno (ENOSPC);
|
|
return -1;
|
|
}
|
|
|
|
if (!is_writing)
|
|
{
|
|
devbufend = devbufstart;
|
|
devbufstart = 0;
|
|
}
|
|
is_writing = 1;
|
|
|
|
if (devbuf)
|
|
{
|
|
while (len > 0)
|
|
{
|
|
if (!varblkop &&
|
|
(len < devbufsiz || devbufend > 0) && devbufend < devbufsiz)
|
|
{
|
|
bytes_to_write = min (len, devbufsiz - devbufend);
|
|
memcpy (devbuf + devbufend, p, bytes_to_write);
|
|
bytes_written += bytes_to_write;
|
|
devbufend += bytes_to_write;
|
|
p += bytes_to_write;
|
|
len -= bytes_to_write;
|
|
}
|
|
else
|
|
{
|
|
if (varblkop)
|
|
{
|
|
bytes_to_write = len;
|
|
tgt = p;
|
|
}
|
|
else if (devbufend == devbufsiz)
|
|
{
|
|
bytes_to_write = devbufsiz;
|
|
tgt = devbuf;
|
|
}
|
|
else
|
|
{
|
|
bytes_to_write = (len / devbufsiz) * devbufsiz;
|
|
tgt = p;
|
|
}
|
|
|
|
ret = 0;
|
|
write_file (get_handle (), tgt, bytes_to_write, &written, &ret);
|
|
if (written)
|
|
has_written = 1;
|
|
|
|
if (ret)
|
|
{
|
|
if (!is_eom (ret))
|
|
{
|
|
__seterrno ();
|
|
return -1;
|
|
}
|
|
|
|
eom_detected = 1;
|
|
|
|
if (!written && !bytes_written)
|
|
{
|
|
set_errno (ENOSPC);
|
|
return -1;
|
|
}
|
|
|
|
if (tgt == p)
|
|
bytes_written += written;
|
|
|
|
break; // from while (len > 0)
|
|
}
|
|
|
|
if (tgt == devbuf)
|
|
{
|
|
if (written != devbufsiz)
|
|
memmove (devbuf, devbuf + written, devbufsiz - written);
|
|
devbufend = devbufsiz - written;
|
|
}
|
|
else
|
|
{
|
|
len -= written;
|
|
p += written;
|
|
bytes_written += written;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (len > 0)
|
|
{
|
|
if (!write_file (get_handle (), ptr, len, &bytes_written, &ret))
|
|
{
|
|
if (bytes_written)
|
|
has_written = 1;
|
|
if (!is_eom (ret))
|
|
{
|
|
__seterrno ();
|
|
return -1;
|
|
}
|
|
eom_detected = 1;
|
|
if (!bytes_written)
|
|
{
|
|
set_errno (ENOSPC);
|
|
return -1;
|
|
}
|
|
}
|
|
has_written = 1;
|
|
}
|
|
return bytes_written;
|
|
}
|
|
|
|
int
|
|
fhandler_dev_raw::dup (fhandler_base *child)
|
|
{
|
|
int ret = fhandler_base::dup (child);
|
|
|
|
if (! ret)
|
|
{
|
|
fhandler_dev_raw *fhc = (fhandler_dev_raw *) child;
|
|
|
|
fhc->devbufsiz = devbufsiz;
|
|
if (devbufsiz > 1L)
|
|
fhc->devbuf = new char [devbufsiz];
|
|
fhc->devbufstart = 0;
|
|
fhc->devbufend = 0;
|
|
fhc->eom_detected = eom_detected;
|
|
fhc->eof_detected = eof_detected;
|
|
fhc->lastblk_to_read = 0;
|
|
fhc->varblkop = varblkop;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
fhandler_dev_raw::fixup_after_fork (HANDLE)
|
|
{
|
|
devbufstart = 0;
|
|
devbufend = 0;
|
|
lastblk_to_read = 0;
|
|
}
|
|
|
|
void
|
|
fhandler_dev_raw::fixup_after_exec ()
|
|
{
|
|
if (devbufsiz > 1L)
|
|
devbuf = new char [devbufsiz];
|
|
devbufstart = 0;
|
|
devbufend = 0;
|
|
lastblk_to_read = 0;
|
|
}
|
|
|
|
int
|
|
fhandler_dev_raw::ioctl (unsigned int cmd, void *buf)
|
|
{
|
|
int ret = NO_ERROR;
|
|
|
|
if (cmd == RDIOCDOP)
|
|
{
|
|
struct rdop *op = (struct rdop *) buf;
|
|
|
|
if (!op)
|
|
ret = ERROR_INVALID_PARAMETER;
|
|
else
|
|
switch (op->rd_op)
|
|
{
|
|
case RDSETBLK:
|
|
if (get_major () == DEV_TAPE_MAJOR)
|
|
{
|
|
struct mtop mop;
|
|
|
|
mop.mt_op = MTSETBLK;
|
|
mop.mt_count = op->rd_parm;
|
|
ret = ioctl (MTIOCTOP, &mop);
|
|
}
|
|
else if (op->rd_parm % 512)
|
|
ret = ERROR_INVALID_PARAMETER;
|
|
else if (devbuf && op->rd_parm < devbufend - devbufstart)
|
|
ret = ERROR_INVALID_PARAMETER;
|
|
else if (!devbuf || op->rd_parm != devbufsiz)
|
|
{
|
|
char *buf = new char [op->rd_parm];
|
|
if (devbufsiz > 1L)
|
|
{
|
|
memcpy (buf, devbuf + devbufstart, devbufend - devbufstart);
|
|
devbufend -= devbufstart;
|
|
delete [] devbuf;
|
|
}
|
|
else
|
|
devbufend = 0;
|
|
|
|
devbufstart = 0;
|
|
devbuf = buf;
|
|
devbufsiz = op->rd_parm;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else if (cmd == RDIOCGET)
|
|
{
|
|
struct rdget *get = (struct rdget *) buf;
|
|
|
|
if (!get)
|
|
ret = ERROR_INVALID_PARAMETER;
|
|
else
|
|
get->bufsiz = devbufsiz ? devbufsiz : 1L;
|
|
}
|
|
else
|
|
return fhandler_base::ioctl (cmd, buf);
|
|
|
|
if (ret != NO_ERROR)
|
|
{
|
|
SetLastError (ret);
|
|
__seterrno ();
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|