2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
19 #include <afs/errmap_nt.h>
23 /* WinNT-specific implementation of user space device I/O for WinNT devices. */
25 /* This module uses the following usd_handle fields:
26 * handle -- the Win32 (HANDLE) returned by CreateFile.
27 * fullPathName -- allocated ptr (char *) to pathname used to open device.
29 * privateData -- Device size (afs_uint32) in 1Kb units.
32 /* Module-specific constants */
33 #define TAPEOP_RETRYMAX 5
35 #define TRANSIENT_TAPE_ERROR(err) \
36 ((err) == ERROR_BUS_RESET || (err) == ERROR_MEDIA_CHANGED)
38 /* Interface Functions */
41 usd_DeviceRead(usd_handle_t usd
, char *buf
, afs_uint32 nbytes
,
44 HANDLE fd
= usd
->handle
;
52 if (ReadFile(fd
, buf
, nbytes
, xferdP
, NULL
)) {
53 /* read was successful */
57 return nterr_nt2unix(GetLastError(), EIO
);
62 usd_DeviceWrite(usd_handle_t usd
, char *buf
, afs_uint32 nbytes
,
65 HANDLE fd
= usd
->handle
;
70 xferdP
= &bytesWritten
;
74 if (WriteFile(fd
, buf
, nbytes
, xferdP
, NULL
)) {
75 /* write was successful */
79 nterr
= GetLastError();
80 if (nterr
== ERROR_END_OF_MEDIA
) {
82 return 0; /* indicate end of tape condition */
84 return nterr_nt2unix(nterr
, EIO
);
90 * TODO -- Episode tries to determine the size of a disk device
91 * empirically by binary searching for the last region it can successfully
92 * read from the device. It uses only USD_SEEK and USD_READ operations, so
93 * this is portable as long as the underlying device driver fails
94 * gracefully when an attempt is made to access past the end of the device.
95 * Unfortunately this fails on WinNT when accessing a floppy disk. Reads
96 * from floppy disks at tremendous offsets blithely succeed. Luckily, the
97 * Win32 interface provides a way to determine the disk size.
99 * Add a check of the offset against the disk size to fix this problem. */
102 usd_DeviceSeek(usd_handle_t usd
, afs_int64 reqOff
, int whence
,
105 HANDLE fd
= usd
->handle
;
106 DWORD method
, result
;
108 LARGE_INTEGER offset
, retOffset
;
110 /* determine move method based on value of whence */
112 if (whence
== SEEK_SET
) {
114 } else if (whence
== SEEK_CUR
) {
115 method
= FILE_CURRENT
;
116 } else if (whence
== SEEK_END
) {
119 /* whence is invalid */
122 _ASSERT(sizeof(DWORD
) == 4);
126 if (usd
->privateData
) {
128 /* For disk devices that know their size, check the offset against the
129 * limit provided by DeviceIoControl(). */
131 afs_int64 k
= ((afs_uint32
) usd
->privateData
);
133 if (reqOff
>= (k
<< 10))
137 offset
.QuadPart
= reqOff
;
139 result
= SetFilePointerEx(fd
, offset
, &retOffset
, method
);
140 if (result
== 0 && (error
= GetLastError()) != NO_ERROR
)
141 return nterr_nt2unix(error
, EIO
);
144 *curOffP
= retOffset
.QuadPart
;
150 usd_DeviceIoctl(usd_handle_t usd
, int req
, void *arg
)
152 HANDLE fd
= usd
->handle
;
159 case USD_IOCTL_GETTYPE
:
163 BY_HANDLE_FILE_INFORMATION info
;
169 if (!GetFileInformationByHandle(fd
, &info
))
170 fileError
= GetLastError();
173 (fd
, IOCTL_DISK_GET_DRIVE_GEOMETRY
, NULL
, 0, &geom
,
174 sizeof(geom
), &nbytes
, NULL
))
175 diskError
= GetLastError();
178 if ((fileError
== ERROR_INVALID_PARAMETER
179 || fileError
== ERROR_INVALID_FUNCTION
)
181 mode
= S_IFCHR
; /* a disk device */
182 if ((afs_uint32
) (usd
->privateData
) == 0) {
184 /* Fill in the device size from disk geometry info. Note
185 * that this is the whole disk, not just the partition, so
186 * it will serve only as an upper bound. */
188 DWORDLONG size
= geom
.Cylinders
.QuadPart
;
190 size
*= geom
.TracksPerCylinder
;
191 size
*= geom
.SectorsPerTrack
;
192 size
*= geom
.BytesPerSector
;
195 size
>>= 10; /* convert to Kilobytes */
199 k
= (afs_uint32
) size
;
200 usd
->privateData
= (void *)k
;
202 } else if (diskError
== ERROR_INVALID_PARAMETER
&& fileError
== 0)
203 mode
= S_IFREG
; /* a regular file */
205 /* check to see if device is a tape drive */
206 result
= GetTapeStatus(fd
);
208 if (result
!= ERROR_INVALID_FUNCTION
209 && result
!= ERROR_INVALID_PARAMETER
) {
210 /* looks like a tape drive */
221 case USD_IOCTL_GETDEV
:
226 case USD_IOCTL_GETFULLNAME
:
227 *(char **)arg
= usd
->fullPathName
;
230 case USD_IOCTL_GETSIZE
:
231 if (!GetFileSizeEx(fd
, &size
) && (code
= GetLastError()) != NO_ERROR
)
232 return nterr_nt2unix(code
, EIO
);
234 *(afs_int64
*) arg
= size
.QuadPart
;
238 case USD_IOCTL_SETSIZE
:
239 code
= usd_DeviceSeek(usd
, *(afs_int64
*) arg
, SEEK_SET
, NULL
);
241 if (!SetEndOfFile(fd
))
242 code
= nterr_nt2unix(GetLastError(), EIO
);
246 case USD_IOCTL_TAPEOPERATION
:
248 TAPE_GET_MEDIA_PARAMETERS mediaParam
;
249 TAPE_GET_DRIVE_PARAMETERS driveParam
;
250 DWORD mediaParamSize
= sizeof(TAPE_GET_MEDIA_PARAMETERS
);
251 DWORD driveParamSize
= sizeof(TAPE_GET_DRIVE_PARAMETERS
);
252 DWORD reloffset
, fmarkType
;
254 usd_tapeop_t
*tapeOpp
= (usd_tapeop_t
*) arg
;
256 /* Execute specified tape command */
258 switch (tapeOpp
->tp_op
) {
260 /* Determine type of filemark supported by device */
262 GetTapeParameters(fd
, GET_TAPE_DRIVE_INFORMATION
,
263 &driveParamSize
, &driveParam
);
265 if (result
== NO_ERROR
) {
266 /* drive must support either normal or long filemarks */
267 if (driveParam
.FeaturesHigh
& TAPE_DRIVE_WRITE_FILEMARKS
) {
268 fmarkType
= TAPE_FILEMARKS
;
269 } else if (driveParam
.
270 FeaturesHigh
& TAPE_DRIVE_WRITE_LONG_FMKS
) {
271 fmarkType
= TAPE_LONG_FILEMARKS
;
273 result
= ERROR_NOT_SUPPORTED
;
277 /* Write specified number of filemarks */
278 if (result
== NO_ERROR
) {
280 WriteTapemark(fd
, fmarkType
, tapeOpp
->tp_count
,
289 /* absorb non-persistant errors, e.g. ERROR_MEDIA_CHANGED. */
290 result
= SetTapePosition(fd
, TAPE_REWIND
, 0, 0, 0, FALSE
);
291 } while ((result
!= NO_ERROR
)
292 && (retrycount
++ < TAPEOP_RETRYMAX
));
298 /* Space over specified number of file marks */
299 if (tapeOpp
->tp_count
< 0) {
300 result
= ERROR_INVALID_PARAMETER
;
302 if (tapeOpp
->tp_op
== USDTAPE_FSF
) {
303 reloffset
= tapeOpp
->tp_count
;
305 reloffset
= 0 - tapeOpp
->tp_count
;
309 SetTapePosition(fd
, TAPE_SPACE_FILEMARKS
, 0,
310 reloffset
, 0, FALSE
);
314 case USDTAPE_PREPARE
:
315 /* Prepare tape drive for operation; do after open. */
319 /* absorb non-persistant errors */
320 if (retrycount
> 0) {
323 result
= PrepareTape(fd
, TAPE_LOCK
, FALSE
);
324 } while (TRANSIENT_TAPE_ERROR(result
)
325 && retrycount
++ < TAPEOP_RETRYMAX
);
327 if (result
== NO_ERROR
) {
330 /* absorb non-persistant errors */
331 if (retrycount
> 0) {
334 result
= GetTapeStatus(fd
);
335 } while (TRANSIENT_TAPE_ERROR(result
)
336 && retrycount
++ < TAPEOP_RETRYMAX
);
339 /* Querying media/drive info seems to clear bad tape state */
340 if (result
== NO_ERROR
) {
342 GetTapeParameters(fd
, GET_TAPE_MEDIA_INFORMATION
,
343 &mediaParamSize
, &mediaParam
);
346 if (result
== NO_ERROR
) {
348 GetTapeParameters(fd
, GET_TAPE_DRIVE_INFORMATION
,
349 &driveParamSize
, &driveParam
);
353 case USDTAPE_SHUTDOWN
:
354 /* Decommission tape drive after operation; do before close. */
355 result
= PrepareTape(fd
, TAPE_UNLOCK
, FALSE
);
359 /* Invalid command */
360 result
= ERROR_INVALID_PARAMETER
;
364 if (result
== NO_ERROR
) {
367 return nterr_nt2unix(result
, EIO
);
371 case USD_IOCTL_GETBLKSIZE
:
372 *((long *)arg
) = (long)4096;
383 usd_DeviceClose(usd_handle_t usd
)
385 HANDLE fd
= usd
->handle
;
391 code
= nterr_nt2unix(GetLastError(), EIO
);
393 if (usd
->fullPathName
)
394 free(usd
->fullPathName
);
401 * usd_DeviceOpen() -- open WinNT device (or regular file)
404 * oflag -- Various combinations of USD_OPEN_XXX defined in usd.h.
405 * pmode -- ignored; file's security descriptor set to default on create.
406 * usdP -- if NULL device is immediately closed after being opened.
407 * Otherwise, *usdP is set to the user space device handle.
409 * RETURN CODES -- On error a unix-style errno value is *returned*. Else zero.
413 usd_DeviceOpen(const char *path
, int oflag
, int pmode
, usd_handle_t
* usdP
)
416 DWORD access
, share
, create
, attr
;
418 int mode
; /* type of opened object */
424 /* set access as specified in oflag */
426 if (oflag
& USD_OPEN_SYNC
)
427 attr
= FILE_FLAG_WRITE_THROUGH
;
431 /* should we always set:
432 * FILE_FLAG_NO_BUFFERING?
433 * FILE_FLAG_RANDOM_ACCESS?
436 access
= GENERIC_READ
;
437 if (oflag
& USD_OPEN_RDWR
)
438 access
|= GENERIC_WRITE
;
440 /* set create as specified in oflag */
442 if (oflag
& USD_OPEN_CREATE
) {
443 /* must be opening a file; allow it to be created */
444 create
= OPEN_ALWAYS
;
446 create
= OPEN_EXISTING
;
450 if (oflag
& (USD_OPEN_RLOCK
| USD_OPEN_WLOCK
)) {
452 /* make sure both lock bits aren't set */
453 _ASSERT(~oflag
& (USD_OPEN_RLOCK
| USD_OPEN_WLOCK
));
456 ((oflag
& USD_OPEN_RLOCK
) ? FILE_SHARE_READ
: 0 /*no sharing */ );
459 share
= FILE_SHARE_READ
+ FILE_SHARE_WRITE
;
462 /* attempt to open the device/file */
464 devhandle
= CreateFile(path
, access
, share
, NULL
, create
, attr
, NULL
);
466 if (devhandle
== INVALID_HANDLE_VALUE
)
467 return nterr_nt2unix(GetLastError(), EIO
);
469 usd
= calloc(1, sizeof(*usd
));
471 _ASSERT(sizeof(devhandle
) <= sizeof(usd
->handle
));
472 usd
->handle
= (void *)devhandle
;
474 usd
->read
= usd_DeviceRead
;
475 usd
->write
= usd_DeviceWrite
;
476 usd
->seek
= usd_DeviceSeek
;
477 usd
->ioctl
= usd_DeviceIoctl
;
478 usd
->close
= usd_DeviceClose
;
480 usd
->fullPathName
= strdup(path
);
481 usd
->openFlags
= oflag
;
483 /* For devices, this is the first real reference, so many errors show up
484 * here. Also this call also sets the size (stored in usd->privateData)
485 * based on the results of the call to DeviceIoControl(). */
486 code
= USD_IOCTL(usd
, USD_IOCTL_GETTYPE
, &mode
);
489 /* If we're trying to obtain a write lock on a real disk, then the
490 * aggregate must not be attached by the kernel. If so, unlock it
492 * WARNING: The code to check for the above has been removed when this
493 * file was ported from DFS src. It should be put back if
494 * this library is used to access hard disks
497 if (code
== 0 && usdP
)
500 usd_DeviceClose(usd
);
505 usd_Open(const char *path
, int oflag
, int mode
, usd_handle_t
* usdP
)
507 return usd_DeviceOpen(path
, oflag
, mode
, usdP
);
511 usd_DeviceDummyClose(usd_handle_t usd
)
518 usd_DeviceStandardInput(usd_handle_t
* usdP
)
525 usd
= calloc(1, sizeof(*usd
));
526 usd
->handle
= (void *)0;
527 usd
->read
= usd_DeviceRead
;
528 usd
->write
= usd_DeviceWrite
;
529 usd
->seek
= usd_DeviceSeek
;
530 usd
->ioctl
= usd_DeviceIoctl
;
531 usd
->close
= usd_DeviceDummyClose
;
532 usd
->fullPathName
= "STDIN";
539 usd_StandardInput(usd_handle_t
* usdP
)
541 return usd_DeviceStandardInput(usdP
);
545 usd_DeviceStandardOutput(usd_handle_t
* usdP
)
552 usd
= calloc(1, sizeof(*usd
));
553 usd
->handle
= (void *)1;
554 usd
->read
= usd_DeviceRead
;
555 usd
->write
= usd_DeviceWrite
;
556 usd
->seek
= usd_DeviceSeek
;
557 usd
->ioctl
= usd_DeviceIoctl
;
558 usd
->close
= usd_DeviceDummyClose
;
559 usd
->fullPathName
= "STDOUT";
566 usd_StandardOutput(usd_handle_t
* usdP
)
568 return usd_DeviceStandardOutput(usdP
);