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>
20 #include <afs/errmap_nt.h>
24 /* WinNT-specific implementation of user space device I/O for WinNT devices. */
26 /* This module uses the following usd_handle fields:
27 * handle -- the Win32 (HANDLE) returned by CreateFile.
28 * fullPathName -- allocated ptr (char *) to pathname used to open device.
30 * privateData -- Device size (afs_uint32) in 1Kb units.
33 /* Module-specific constants */
34 #define TAPEOP_RETRYMAX 5
36 #define TRANSIENT_TAPE_ERROR(err) \
37 ((err) == ERROR_BUS_RESET || (err) == ERROR_MEDIA_CHANGED)
39 /* Interface Functions */
42 usd_DeviceRead(usd_handle_t usd, char *buf, afs_uint32 nbytes,
45 HANDLE fd = usd->handle;
53 if (ReadFile(fd, buf, nbytes, xferdP, NULL)) {
54 /* read was successful */
58 return nterr_nt2unix(GetLastError(), EIO);
63 usd_DeviceWrite(usd_handle_t usd, char *buf, afs_uint32 nbytes,
66 HANDLE fd = usd->handle;
71 xferdP = &bytesWritten;
75 if (WriteFile(fd, buf, nbytes, xferdP, NULL)) {
76 /* write was successful */
80 nterr = GetLastError();
81 if (nterr == ERROR_END_OF_MEDIA) {
83 return 0; /* indicate end of tape condition */
85 return nterr_nt2unix(nterr, EIO);
91 * TODO -- Episode tries to determine the size of a disk device
92 * empirically by binary searching for the last region it can successfully
93 * read from the device. It uses only USD_SEEK and USD_READ operations, so
94 * this is portable as long as the underlying device driver fails
95 * gracefully when an attempt is made to access past the end of the device.
96 * Unfortunately this fails on WinNT when accessing a floppy disk. Reads
97 * from floppy disks at tremendous offsets blithely succeed. Luckily, the
98 * Win32 interface provides a way to determine the disk size.
100 * Add a check of the offset against the disk size to fix this problem. */
103 usd_DeviceSeek(usd_handle_t usd, afs_hyper_t reqOff, int whence,
104 afs_hyper_t * curOffP)
106 HANDLE fd = usd->handle;
107 DWORD method, result;
109 long loOffset, hiOffset;
111 /* determine move method based on value of whence */
113 if (whence == SEEK_SET) {
115 } else if (whence == SEEK_CUR) {
116 method = FILE_CURRENT;
117 } else if (whence == SEEK_END) {
120 /* whence is invalid */
123 _ASSERT(sizeof(DWORD) == 4);
127 loOffset = hgetlo(reqOff);
128 hiOffset = hgethi(reqOff);
130 if (usd->privateData) {
132 /* For disk devices that know their size, check the offset against the
133 * limit provided by DeviceIoControl(). */
136 ((DWORDLONG) hgethi(reqOff) << 32 | (DWORDLONG) hgetlo(reqOff));
138 DWORDLONG k = (DWORDLONG) ((afs_uint32) usd->privateData);
140 if (offset >= (k << 10))
144 result = SetFilePointer(fd, loOffset, &hiOffset, method);
145 if (result == 0xffffffff && (error = GetLastError()) != NO_ERROR)
146 return nterr_nt2unix(error, EIO);
148 hset64(*curOffP, hiOffset, result);
154 usd_DeviceIoctl(usd_handle_t usd, int req, void *arg)
156 HANDLE fd = usd->handle;
162 case USD_IOCTL_GETTYPE:
166 BY_HANDLE_FILE_INFORMATION info;
172 if (!GetFileInformationByHandle(fd, &info))
173 fileError = GetLastError();
176 (fd, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geom,
177 sizeof(geom), &nbytes, NULL))
178 diskError = GetLastError();
181 if ((fileError == ERROR_INVALID_PARAMETER
182 || fileError == ERROR_INVALID_FUNCTION)
184 mode = S_IFCHR; /* a disk device */
185 if ((afs_uint32) (usd->privateData) == 0) {
187 /* Fill in the device size from disk geometry info. Note
188 * that this is the whole disk, not just the partition, so
189 * it will serve only as an upper bound. */
191 DWORDLONG size = geom.Cylinders.QuadPart;
193 size *= geom.TracksPerCylinder;
194 size *= geom.SectorsPerTrack;
195 size *= geom.BytesPerSector;
198 size >>= 10; /* convert to Kilobytes */
202 k = (afs_uint32) size;
203 usd->privateData = (void *)k;
205 } else if (diskError == ERROR_INVALID_PARAMETER && fileError == 0)
206 mode = S_IFREG; /* a regular file */
208 /* check to see if device is a tape drive */
209 result = GetTapeStatus(fd);
211 if (result != ERROR_INVALID_FUNCTION
212 && result != ERROR_INVALID_PARAMETER) {
213 /* looks like a tape drive */
224 case USD_IOCTL_GETDEV:
229 case USD_IOCTL_GETFULLNAME:
230 *(char **)arg = usd->fullPathName;
233 case USD_IOCTL_GETSIZE:
234 result = GetFileSize(fd, &hiPart);
235 if (result == 0xffffffff && (code = GetLastError()) != NO_ERROR)
236 return nterr_nt2unix(code, EIO);
237 hset64(*(afs_hyper_t *) arg, hiPart, result);
240 case USD_IOCTL_SETSIZE:
241 code = usd_DeviceSeek(usd, *(afs_hyper_t *) arg, SEEK_SET, NULL);
243 if (!SetEndOfFile(fd))
244 code = nterr_nt2unix(GetLastError(), EIO);
248 case USD_IOCTL_TAPEOPERATION:
250 TAPE_GET_MEDIA_PARAMETERS mediaParam;
251 TAPE_GET_DRIVE_PARAMETERS driveParam;
252 DWORD mediaParamSize = sizeof(TAPE_GET_MEDIA_PARAMETERS);
253 DWORD driveParamSize = sizeof(TAPE_GET_DRIVE_PARAMETERS);
254 DWORD reloffset, fmarkType;
256 usd_tapeop_t *tapeOpp = (usd_tapeop_t *) arg;
258 /* Execute specified tape command */
260 switch (tapeOpp->tp_op) {
262 /* Determine type of filemark supported by device */
264 GetTapeParameters(fd, GET_TAPE_DRIVE_INFORMATION,
265 &driveParamSize, &driveParam);
267 if (result == NO_ERROR) {
268 /* drive must support either normal or long filemarks */
269 if (driveParam.FeaturesHigh & TAPE_DRIVE_WRITE_FILEMARKS) {
270 fmarkType = TAPE_FILEMARKS;
271 } else if (driveParam.
272 FeaturesHigh & TAPE_DRIVE_WRITE_LONG_FMKS) {
273 fmarkType = TAPE_LONG_FILEMARKS;
275 result = ERROR_NOT_SUPPORTED;
279 /* Write specified number of filemarks */
280 if (result == NO_ERROR) {
282 WriteTapemark(fd, fmarkType, tapeOpp->tp_count,
291 /* absorb non-persistant errors, e.g. ERROR_MEDIA_CHANGED. */
292 result = SetTapePosition(fd, TAPE_REWIND, 0, 0, 0, FALSE);
293 } while ((result != NO_ERROR)
294 && (retrycount++ < TAPEOP_RETRYMAX));
300 /* Space over specified number of file marks */
301 if (tapeOpp->tp_count < 0) {
302 result = ERROR_INVALID_PARAMETER;
304 if (tapeOpp->tp_op == USDTAPE_FSF) {
305 reloffset = tapeOpp->tp_count;
307 reloffset = 0 - tapeOpp->tp_count;
311 SetTapePosition(fd, TAPE_SPACE_FILEMARKS, 0,
312 reloffset, 0, FALSE);
316 case USDTAPE_PREPARE:
317 /* Prepare tape drive for operation; do after open. */
321 /* absorb non-persistant errors */
322 if (retrycount > 0) {
325 result = PrepareTape(fd, TAPE_LOCK, FALSE);
326 } while (TRANSIENT_TAPE_ERROR(result)
327 && retrycount++ < TAPEOP_RETRYMAX);
329 if (result == NO_ERROR) {
332 /* absorb non-persistant errors */
333 if (retrycount > 0) {
336 result = GetTapeStatus(fd);
337 } while (TRANSIENT_TAPE_ERROR(result)
338 && retrycount++ < TAPEOP_RETRYMAX);
341 /* Querying media/drive info seems to clear bad tape state */
342 if (result == NO_ERROR) {
344 GetTapeParameters(fd, GET_TAPE_MEDIA_INFORMATION,
345 &mediaParamSize, &mediaParam);
348 if (result == NO_ERROR) {
350 GetTapeParameters(fd, GET_TAPE_DRIVE_INFORMATION,
351 &driveParamSize, &driveParam);
355 case USDTAPE_SHUTDOWN:
356 /* Decommission tape drive after operation; do before close. */
357 result = PrepareTape(fd, TAPE_UNLOCK, FALSE);
361 /* Invalid command */
362 result = ERROR_INVALID_PARAMETER;
366 if (result == NO_ERROR) {
369 return nterr_nt2unix(result, EIO);
373 case USD_IOCTL_GETBLKSIZE:
374 *((long *)arg) = (long)4096;
385 usd_DeviceClose(usd_handle_t usd)
387 HANDLE fd = usd->handle;
393 code = nterr_nt2unix(GetLastError(), EIO);
395 if (usd->fullPathName)
396 free(usd->fullPathName);
403 * usd_DeviceOpen() -- open WinNT device (or regular file)
406 * oflag -- Various combinations of USD_OPEN_XXX defined in usd.h.
407 * pmode -- ignored; file's security descriptor set to default on create.
408 * usdP -- if NULL device is immediately closed after being opened.
409 * Otherwise, *usdP is set to the user space device handle.
411 * RETURN CODES -- On error a unix-style errno value is *returned*. Else zero.
415 usd_DeviceOpen(const char *path, int oflag, int pmode, usd_handle_t * usdP)
418 DWORD access, share, create, attr;
420 int mode; /* type of opened object */
426 /* set access as specified in oflag */
428 if (oflag & USD_OPEN_SYNC)
429 attr = FILE_FLAG_WRITE_THROUGH;
433 /* should we always set:
434 * FILE_FLAG_NO_BUFFERING?
435 * FILE_FLAG_RANDOM_ACCESS?
438 access = GENERIC_READ;
439 if (oflag & USD_OPEN_RDWR)
440 access |= GENERIC_WRITE;
442 /* set create as specified in oflag */
444 if (oflag & USD_OPEN_CREATE) {
445 /* must be opening a file; allow it to be created */
446 create = OPEN_ALWAYS;
448 create = OPEN_EXISTING;
452 if (oflag & (USD_OPEN_RLOCK | USD_OPEN_WLOCK)) {
454 /* make sure both lock bits aren't set */
455 _ASSERT(~oflag & (USD_OPEN_RLOCK | USD_OPEN_WLOCK));
458 ((oflag & USD_OPEN_RLOCK) ? FILE_SHARE_READ : 0 /*no sharing */ );
461 share = FILE_SHARE_READ + FILE_SHARE_WRITE;
464 /* attempt to open the device/file */
466 devhandle = CreateFile(path, access, share, NULL, create, attr, NULL);
468 if (devhandle == INVALID_HANDLE_VALUE)
469 return nterr_nt2unix(GetLastError(), EIO);
471 usd = (usd_handle_t) malloc(sizeof(*usd));
472 memset(usd, 0, sizeof(*usd));
475 _ASSERT(sizeof(devhandle) <= sizeof(usd->handle));
476 usd->handle = (void *)devhandle;
478 usd->read = usd_DeviceRead;
479 usd->write = usd_DeviceWrite;
480 usd->seek = usd_DeviceSeek;
481 usd->ioctl = usd_DeviceIoctl;
482 usd->close = usd_DeviceClose;
484 usd->fullPathName = (char *)malloc(strlen(path) + 1);
485 strcpy(usd->fullPathName, path);
486 usd->openFlags = oflag;
488 /* For devices, this is the first real reference, so many errors show up
489 * here. Also this call also sets the size (stored in usd->privateData)
490 * based on the results of the call to DeviceIoControl(). */
491 code = USD_IOCTL(usd, USD_IOCTL_GETTYPE, &mode);
494 /* If we're trying to obtain a write lock on a real disk, then the
495 * aggregate must not be attached by the kernel. If so, unlock it
497 * WARNING: The code to check for the above has been removed when this
498 * file was ported from DFS src. It should be put back if
499 * this library is used to access hard disks
502 if (code == 0 && usdP)
505 usd_DeviceClose(usd);
510 usd_Open(const char *path, int oflag, int mode, usd_handle_t * usdP)
512 return usd_DeviceOpen(path, oflag, mode, usdP);
516 usd_DeviceDummyClose(usd_handle_t usd)
523 usd_DeviceStandardInput(usd_handle_t * usdP)
530 usd = (usd_handle_t) malloc(sizeof(*usd));
531 memset(usd, 0, sizeof(*usd));
532 usd->handle = (void *)0;
533 usd->read = usd_DeviceRead;
534 usd->write = usd_DeviceWrite;
535 usd->seek = usd_DeviceSeek;
536 usd->ioctl = usd_DeviceIoctl;
537 usd->close = usd_DeviceDummyClose;
538 usd->fullPathName = "STDIN";
545 usd_StandardInput(usd_handle_t * usdP)
547 return usd_DeviceStandardInput(usdP);
551 usd_DeviceStandardOutput(usd_handle_t * usdP)
558 usd = (usd_handle_t) malloc(sizeof(*usd));
559 memset(usd, 0, sizeof(*usd));
560 usd->handle = (void *)1;
561 usd->read = usd_DeviceRead;
562 usd->write = usd_DeviceWrite;
563 usd->seek = usd_DeviceSeek;
564 usd->ioctl = usd_DeviceIoctl;
565 usd->close = usd_DeviceDummyClose;
566 usd->fullPathName = "STDOUT";
573 usd_StandardOutput(usd_handle_t * usdP)
575 return usd_DeviceStandardOutput(usdP);