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);