1 /*
2 * Copyright (c) 2010-2014 Douglas Gilbert.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30 /* This is a C source file contains Windows specific code for the ddpt
31 * utility.
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #include "config.h"
36 #endif
37
38 #ifdef SG_LIB_WIN32
39
40 #include <unistd.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include <limits.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <fcntl.h>
50 #define __STDC_FORMAT_MACROS 1
51 #include <inttypes.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <sys/time.h>
55
56 #include "ddpt.h" /* includes <signal.h> */
57
58 #include <windows.h>
59 #include <winioctl.h>
60
61 #ifndef SG_LIB_MINGW
62 /* cygwin */
63 #include <sys/ioctl.h>
64 #endif
65
66 #include "sg_lib.h"
67 #include "sg_cmds_basic.h"
68 #include "sg_cmds_extra.h"
69 #include "sg_pt.h"
70
71 #ifdef HAVE_NANOSLEEP
72 #include <time.h>
73 #elif defined(MSC_VER) || defined(__MINGW32__)
74 #define HAVE_MS_SLEEP
75 #endif
76
77
78 #ifndef HAVE_SYSCONF
79 size_t
win32_pagesize(void)80 win32_pagesize(void)
81 {
82 SYSTEM_INFO sys_info;
83
84 GetSystemInfo(&sys_info);
85 return sys_info.dwPageSize;
86 }
87 #endif
88
89 void
win32_sleep_ms(int millisecs)90 win32_sleep_ms(int millisecs)
91 {
92 if (millisecs > 0) {
93 #ifdef HAVE_NANOSLEEP
94 struct timespec request;
95
96 request.tv_sec = millisecs / 1000;
97 request.tv_nsec = (millisecs % 1000) * 1000000;
98 if ((nanosleep(&request, NULL) < 0) && (EINTR != errno))
99 pr2serr("nanosleep: failed, errno=%d\n", errno);
100 #elif defined(HAVE_MS_SLEEP)
101 Sleep(millisecs);
102 #endif
103 }
104 }
105
106 /* Fetches system error message corresponding to errnum,
107 * placing string in b not exceeding blen bytes. Returns
108 * bytes placed in b (excluding trailing NULL) or -1 for
109 * error. MS refers to them as "System Error Codes". */
110 static const char *
win32_errmsg(int errnum,char * b,int blen)111 win32_errmsg(int errnum, char * b, int blen)
112 {
113 LPTSTR err_txt = 0;
114 DWORD errn = errnum;
115 int len = 0;
116
117 if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
118 FORMAT_MESSAGE_FROM_SYSTEM,
119 NULL,
120 errn,
121 0,
122 (LPTSTR)&err_txt,
123 0,
124 NULL) == 0) {
125 snprintf(b, blen, "FormatMessage(errnum=%d) failed", errnum);
126 return b;
127 } else {
128 len = strlen(err_txt);
129 if (len) {
130 if ('\n' == err_txt[len - 1]) {
131 err_txt[len - 1] = '\0';
132 if ((len > 1) && ('\r' == err_txt[len - 2]))
133 err_txt[len - 2] = '\0';
134 len = strlen(err_txt);
135 }
136 }
137 if (len < 1)
138 b[0] = '\0';
139 else if (len < blen)
140 strcpy(b, err_txt);
141 else {
142 strncpy(b, err_txt, blen);
143 if (blen > 0)
144 b[blen - 1] = '\0';
145 }
146 }
147 if (err_txt)
148 LocalFree(err_txt);
149 len = strlen(b);
150 snprintf(b + len, blen - len, " [%d]", errnum);
151 return b;
152 }
153
154 /* Return 1 for filenames starting with '\', or of the form '<letter>:'
155 * or of the form PD<n>, PHYSICALDRIVE<n>, CDROM<n> or TAPE<n>. The <n>
156 * is one or two digits with no following characters. Otherwise return 0. */
157 static int
is_win_blk_dev(const char * fn)158 is_win_blk_dev(const char * fn)
159 {
160 int len, off;
161
162 len = strlen(fn);
163 if ((2 == len) && isalpha((int)fn[0]) && (':' == fn[1]))
164 return 1;
165 if (len < 3)
166 return 0;
167 if ('\\' == fn[0])
168 return 1;
169 if (0 == strncmp(fn, "PD", 2))
170 off = 2;
171 else if (0 == strncmp(fn, "CDROM", 5))
172 off = 5;
173 else if (0 == strncmp(fn, "PHYSICALDRIVE", 13))
174 off = 13;
175 else if (0 == strncmp(fn, "TAPE", 4))
176 off = 4;
177 else
178 return 0;
179
180 if (len <= off)
181 return 0;
182 if (! isdigit((int)fn[off]))
183 return 0;
184 if (len == (off + 1))
185 return 1;
186 if ((len != off + 2) || (! isdigit((int)fn[off + 1])))
187 return 0;
188 else
189 return 1;
190 }
191
192 int
win32_dd_filetype(const char * fn,int verbose)193 win32_dd_filetype(const char * fn, int verbose)
194 {
195 size_t len = strlen(fn);
196
197 if (verbose) { ; } /* suppress warning */
198 if ((1 == len) && ('.' == fn[0]))
199 return FT_DEV_NULL;
200 else if ((3 == len) && (
201 (0 == strcmp("NUL", fn)) || (0 == strcmp("nul", fn))))
202 return FT_DEV_NULL;
203 else if ((len > 8) && (0 == strncmp("\\\\.\\TAPE", fn, 8)))
204 return FT_TAPE;
205 else if ((len > 4) && (0 == strncmp("\\\\.\\", fn, 4)))
206 return FT_BLOCK;
207 else
208 return FT_REG;
209 }
210
211 /* Adjust device file name for Windows; pass-through setup */
212 void
win32_adjust_fns_pt(struct opts_t * op)213 win32_adjust_fns_pt(struct opts_t * op)
214 {
215 char b[INOUTF_SZ];
216 char * fn_arr[2];
217 char * cp;
218 int k, j, len;
219
220 memset(fn_arr, 0 , sizeof(fn_arr));
221 fn_arr[0] = op->idip->fn;
222 fn_arr[1] = op->odip->fn;
223 for (k = 0; k < 2; ++k) {
224 cp = fn_arr[k];
225 if (NULL == cp)
226 continue;
227 len = strlen(cp);
228 if (len < 2)
229 continue;
230 if ('\\' == cp[0])
231 continue;
232 for (j = 0; j < len; ++j)
233 b[j] = toupper((int)cp[j]);
234 b[len] = '\0';
235 if (is_win_blk_dev(b)) {
236 if (0 == strncmp(b, "PD", 2)) {
237 strcpy(cp, "\\\\.\\PHYSICALDRIVE");
238 if (b[2])
239 strncat(cp, b + 2, len - 2);
240 } else {
241 strcpy(cp, "\\\\.\\");
242 strncat(cp, b, len);
243 }
244 }
245 }
246 #ifdef SG_LIB_WIN32_DIRECT
247 if (op->verbose > 4)
248 pr2serr("Initial win32 SPT interface state: %s\n",
249 scsi_pt_win32_spt_state() ? "direct" : "indirect");
250 scsi_pt_win32_direct(SG_LIB_WIN32_DIRECT /* SPT pt interface */);
251 #endif
252 }
253
254 /* Main copy loop's read (input) for win32 block device. Returns 0 on
255 * success, else SG_LIB_FILE_ERROR, SG_LIB_CAT_MEDIUM_HARD or -1 . */
256 int
win32_cp_read_block(struct opts_t * op,struct cp_state_t * csp,unsigned char * bp,int * ifull_extrap,int verbose)257 win32_cp_read_block(struct opts_t * op, struct cp_state_t * csp,
258 unsigned char * bp, int * ifull_extrap, int verbose)
259 {
260 int k, res, res2;
261 int ibs = op->ibs;
262 int64_t offset = op->skip * ibs;
263 int64_t my_skip;
264 int numbytes = csp->icbpt * ibs;
265
266 if (ifull_extrap)
267 *ifull_extrap = 0;
268 if (offset != csp->if_filepos) {
269 if (verbose > 2)
270 pr2serr("moving if filepos: new_pos=%" PRId64 "\n",
271 (int64_t)offset);
272 if (win32_set_file_pos(op, DDPT_ARG_IN, offset, verbose))
273 return SG_LIB_FILE_ERROR;
274 csp->if_filepos = offset;
275 }
276 res = win32_block_read(op, bp, numbytes, verbose);
277 if (res < 0) {
278 if ((-SG_LIB_CAT_MEDIUM_HARD == res) && (op->iflagp->coe)) {
279 if (1 == csp->icbpt) {
280 // Don't read again, this must be bad block
281 memset(bp, 0, ibs);
282 if ((res2 = coe_process_eio(op, op->skip)))
283 return res2;
284 ++*ifull_extrap;
285 csp->bytes_read += ibs;
286 return 0;
287 } else {
288 my_skip = op->skip;
289 for (k = 0; k < csp->icbpt;
290 ++k, ++my_skip, bp += ibs, offset += ibs) {
291 if (offset != csp->if_filepos) {
292 if (verbose > 2)
293 pr2serr("moving if filepos: new_pos=%" PRId64
294 "\n", (int64_t)offset);
295 if (win32_set_file_pos(op, DDPT_ARG_IN, offset,
296 verbose))
297 return SG_LIB_FILE_ERROR;
298 csp->if_filepos = offset;
299 }
300 memset(bp, 0, ibs);
301 res = win32_block_read(op, bp, ibs, verbose);
302 if (ibs == res) {
303 zero_coe_limit_count(op);
304 csp->if_filepos += ibs;
305 if (verbose > 2)
306 pr2serr("reading 1 block, skip=%" PRId64 " : "
307 "okay\n", my_skip);
308 } else if (-SG_LIB_CAT_MEDIUM_HARD == res) {
309 if ((res2 = coe_process_eio(op, my_skip)))
310 return res2;
311 } else {
312 pr2serr("reading 1 block, skip=%" PRId64 " failed\n",
313 my_skip);
314 csp->leave_reason = SG_LIB_CAT_OTHER;
315 csp->icbpt = k;
316 csp->ocbpt = (k * ibs) / op->obs;
317 if (((k * ibs) % op->obs) > 0)
318 ++csp->ocbpt;
319 return 0;
320 }
321 ++*ifull_extrap;
322 csp->bytes_read += ibs;
323 }
324 return 0;
325 }
326 } else {
327 pr2serr("read(win32_block), skip=%" PRId64 " error occurred\n",
328 op->skip);
329 return (-SG_LIB_CAT_MEDIUM_HARD == res) ? -res : -1;
330 }
331 } else {
332 if (res < numbytes) {
333 /* assume no partial reads (i.e. non integral blocks) */
334 csp->icbpt = res / ibs;
335 ++csp->leave_after_write;
336 csp->leave_reason = 0; /* assume at end rather than error */
337 csp->ocbpt = res / op->obs;
338 if (verbose > 1)
339 pr2serr("short read, requested %d blocks, got %d blocks\n",
340 numbytes / ibs, csp->icbpt);
341 }
342 csp->if_filepos += res;
343 if (ifull_extrap)
344 *ifull_extrap = csp->icbpt;
345 }
346 return 0;
347 }
348
349 /* Returns 0 on success, 1 on error */
350 int
win32_open_if(struct opts_t * op,int flags,int verbose)351 win32_open_if(struct opts_t * op, int flags, int verbose)
352 {
353 DISK_GEOMETRY g;
354 DWORD count, share_mode, err;
355 char b[80];
356 int blen;
357
358 blen = sizeof(b);
359 if (verbose)
360 pr2serr("CreateFile(%s , in)\n", op->idip->fn);
361 share_mode = (O_EXCL & flags) ? 0 : (FILE_SHARE_READ | FILE_SHARE_WRITE);
362 op->idip->fh = CreateFile(op->idip->fn,
363 GENERIC_READ | GENERIC_WRITE,
364 share_mode,
365 NULL,
366 OPEN_EXISTING,
367 0,
368 NULL);
369 if (INVALID_HANDLE_VALUE == op->idip->fh) {
370 err = GetLastError();
371 pr2serr("CreateFile(in) failed, %s\n", win32_errmsg(err, b, blen));
372 return 1;
373 }
374 if (0 == DeviceIoControl(op->idip->fh, IOCTL_DISK_GET_DRIVE_GEOMETRY,
375 NULL, 0, &g, sizeof(g), &count, NULL)) {
376 err = GetLastError();
377 pr2serr("DeviceIoControl(in, geometry) failed, %s\n",
378 win32_errmsg(err, b, blen));
379 return 1;
380 }
381 if ((int)g.BytesPerSector != op->ibs) {
382 pr2serr("Specified in block size (%d) doesn't match device geometry "
383 "block size: %d\n", op->ibs, (int)g.BytesPerSector);
384 return 1;
385 }
386 return 0;
387 }
388
389 /* Returns 0 on success, 1 on error. */
390 int
win32_open_of(struct opts_t * op,int flags,int verbose)391 win32_open_of(struct opts_t * op, int flags, int verbose)
392 {
393 DISK_GEOMETRY g;
394 DWORD count, share_mode, err;
395 char b[80];
396 int blen;
397
398 blen = sizeof(b);
399 if (verbose)
400 pr2serr("CreateFile(%s , out)\n", op->odip->fn);
401 share_mode = (O_EXCL & flags) ? 0 : (FILE_SHARE_READ | FILE_SHARE_WRITE);
402 op->odip->fh = CreateFile(op->odip->fn,
403 GENERIC_READ | GENERIC_WRITE,
404 share_mode,
405 NULL,
406 OPEN_EXISTING,
407 0,
408 NULL);
409 if (INVALID_HANDLE_VALUE == op->odip->fh) {
410 err = GetLastError();
411 pr2serr("CreateFile(out) failed, %s\n", win32_errmsg(err, b, blen));
412 return 1;
413 }
414 if (0 == DeviceIoControl(op->odip->fh, IOCTL_DISK_GET_DRIVE_GEOMETRY,
415 NULL, 0, &g, sizeof(g), &count, NULL)) {
416 err = GetLastError();
417 pr2serr("DeviceIoControl(out, geometry) failed, %s\n",
418 win32_errmsg(err, b, blen));
419 return 1;
420 }
421 if ((int)g.BytesPerSector != op->obs) {
422 pr2serr("Specified out block size (%d) doesn't match device geometry "
423 "block size: %d\n", op->obs,
424 (int)g.BytesPerSector);
425 return 1;
426 }
427 return 0;
428 }
429
430 /* Returns 0 on success, 1 on error */
431 int
win32_set_file_pos(struct opts_t * op,int which_arg,int64_t pos,int verbose)432 win32_set_file_pos(struct opts_t * op, int which_arg, int64_t pos,
433 int verbose)
434 {
435 LONG lo32 = pos & 0xffffffff;
436 LONG hi32 = (pos >> 32) & 0xffffffff;
437 DWORD err;
438 DWORD lo_ret;
439 HANDLE fh;
440 const char * cp;
441 char b[80];
442 int blen;
443
444 blen = sizeof(b);
445 fh = (DDPT_ARG_IN == which_arg) ? op->idip->fh : op->odip->fh;
446 cp = (DDPT_ARG_IN == which_arg) ? "in" : "out";
447 if (verbose > 2)
448 pr2serr("SetFilePointer( 0x%" PRIx64 ", %s)\n", pos, cp);
449 lo_ret = SetFilePointer(fh, lo32, &hi32, FILE_BEGIN);
450 if ((INVALID_SET_FILE_POINTER == lo_ret) &&
451 (NO_ERROR != (err = GetLastError()))) {
452 if (verbose)
453 pr2serr("SetFilePointer failed to set pos=[0x%" PRIx64 "], %s\n",
454 pos, win32_errmsg(err, b, blen));
455 return 1;
456 }
457 return 0;
458 }
459
460 /* Returns number read, -SG_LIB_CAT_MEDIUM_HARD or -1 on error */
461 int
win32_block_read(struct opts_t * op,unsigned char * bp,int num_bytes,int verbose)462 win32_block_read(struct opts_t * op, unsigned char * bp, int num_bytes,
463 int verbose)
464 {
465 DWORD num = num_bytes;
466 DWORD howMany, err;
467 char b[80];
468 int blen;
469
470 blen = sizeof(b);
471 if (verbose > 2)
472 pr2serr("ReadFile(num=%d, in)\n", num_bytes);
473 if (ReadFile(op->idip->fh, bp, num, &howMany, NULL) == 0) {
474 err = GetLastError();
475 if (verbose)
476 pr2serr("ReadFile failed, %s\n", win32_errmsg(err, b, blen));
477 if (23 == err)
478 return -SG_LIB_CAT_MEDIUM_HARD;
479 else
480 return -1;
481 }
482 return (int)howMany;
483 }
484
485 /* Returns number read from OFILE, -SG_LIB_CAT_MEDIUM_HARD or -1 on error */
486 int
win32_block_read_from_of(struct opts_t * op,unsigned char * bp,int num_bytes,int verbose)487 win32_block_read_from_of(struct opts_t * op, unsigned char * bp,
488 int num_bytes, int verbose)
489 {
490 DWORD num = num_bytes;
491 DWORD howMany, err;
492 char b[80];
493 int blen;
494
495 blen = sizeof(b);
496 if (verbose > 2)
497 pr2serr("ReadFile(num=%d, out)\n", num_bytes);
498 if (ReadFile(op->odip->fh, bp, num, &howMany, NULL) == 0) {
499 err = GetLastError();
500 if (verbose)
501 pr2serr("ReadFile(from_of) failed, %s\n",
502 win32_errmsg(err, b, blen));
503 if (23 == err)
504 return -SG_LIB_CAT_MEDIUM_HARD;
505 else
506 return -1;
507 }
508 return (int)howMany;
509 }
510
511 /* Returns number written, -SG_LIB_CAT_MEDIUM_HARD or -1 on error */
512 int
win32_block_write(struct opts_t * op,const unsigned char * bp,int num_bytes,int verbose)513 win32_block_write(struct opts_t * op, const unsigned char * bp,
514 int num_bytes, int verbose)
515 {
516 DWORD num = num_bytes;
517 DWORD howMany, err;
518 char b[80];
519 int blen;
520
521 blen = sizeof(b);
522 if (verbose > 2)
523 pr2serr("WriteFile(num=%d, out)\n", num_bytes);
524 if (WriteFile(op->odip->fh, bp, num, &howMany, NULL) == 0) {
525 err = GetLastError();
526 if (verbose)
527 pr2serr("WriteFile failed, %s\n", win32_errmsg(err, b, blen));
528 if (23 == err)
529 return -SG_LIB_CAT_MEDIUM_HARD;
530 else
531 return -1;
532 }
533 return (int)howMany;
534 }
535
536 /* win32_get_blkdev_capacity() returns 0 -> success or -1 -> failure. If
537 * successful writes back sector size (logical block size) using the sect_sz
538 * pointer. Also writes back the number of sectors (logical blocks) on the
539 * block device using num_sect pointer. Win32 version. */
540 int
win32_get_blkdev_capacity(struct opts_t * op,int which_arg,int64_t * num_sect,int * sect_sz)541 win32_get_blkdev_capacity(struct opts_t * op, int which_arg,
542 int64_t * num_sect, int * sect_sz)
543 {
544 DISK_GEOMETRY g;
545 GET_LENGTH_INFORMATION gli;
546 ULARGE_INTEGER total_bytes;
547 DWORD count, err;
548 HANDLE fh;
549 const char * fname;
550 int64_t byte_len, blks;
551 int fname_len;
552 char dirName[64];
553 char b[80];
554 int blen;
555
556 blen = sizeof(b);
557 fh = (DDPT_ARG_IN == which_arg) ? op->idip->fh : op->odip->fh;
558 fname = (DDPT_ARG_IN == which_arg) ? op->idip->fn : op->odip->fn;
559 if (op->verbose > 2)
560 pr2serr("win32_get_blkdev_capacity: for %s\n", fname);
561 if (0 == DeviceIoControl(fh, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &g,
562 sizeof(g), &count, NULL)) {
563 if (op->verbose) {
564 err = GetLastError();
565 pr2serr("DeviceIoControl(blkdev, geometry) failed, %s\n",
566 win32_errmsg(err, b, blen));
567 }
568 *num_sect = 0;
569 *sect_sz = 0;
570 return -1;
571 }
572 *sect_sz = (int)g.BytesPerSector;
573
574 /* IOCTL_DISK_GET_LENGTH_INFO not defined before XP */
575 if (DeviceIoControl(fh, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli,
576 sizeof(gli), &count, NULL)) {
577 byte_len = gli.Length.QuadPart;
578 *num_sect = byte_len / (int)g.BytesPerSector;
579 return 0;
580 } else if (op->verbose > 2) {
581 err = GetLastError();
582 pr2serr("DeviceIoControl(blkdev, length_info) failed, %s\n",
583 win32_errmsg(err, b, blen));
584 }
585
586 /* Assume if device name finishes in digit then its physical */
587 fname_len = (int)strlen(fname);
588 if (isdigit((int)fname[fname_len - 1])) {
589 blks = g.Cylinders.QuadPart;
590 blks *= g.TracksPerCylinder;
591 blks *= g.SectorsPerTrack;
592 *num_sect = blks;
593 return 0;
594 }
595 if ((fname_len < 4) || (fname_len > (int)sizeof(dirName))) {
596 pr2serr("win32_get_blkdev_capacity: unable to process %s into "
597 "directory name\n", fname);
598 *num_sect = 0;
599 return -1;
600 }
601 memcpy(dirName, fname + 4, fname_len - 4);
602 dirName[fname_len - 4] = '\\';
603 dirName[fname_len - 3] = '\0';
604
605 if (GetDiskFreeSpaceEx(dirName, NULL, &total_bytes, NULL)) {
606 byte_len = total_bytes.QuadPart;
607 *num_sect = byte_len / (int)g.BytesPerSector;
608 } else {
609 if (op->verbose > 1) {
610 err = GetLastError();
611 pr2serr("GetDiskFreeSpaceEx(%s) failed, %s\n", dirName,
612 win32_errmsg(err, b, blen));
613 }
614 *num_sect = 0;
615 return -1;
616 }
617 return 0;
618 }
619
620 #endif
621