source: trunk/src/helpers/dosh2.c@ 14

Last change on this file since 14 was 14, checked in by umoeller, 25 years ago

Major updates; timers, LVM, miscellaneous.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 69.4 KB
Line 
1
2/*
3 *@@sourcefile dosh2.c:
4 * dosh.c contains more Control Program helper functions.
5 *
6 * This file is new with V0.9.4 (2000-07-26) [umoeller].
7 *
8 * As opposed to the functions in dosh.c, these require
9 * linking against other helpers. As a result, these have
10 * been separated from dosh.c to allow linking against
11 * dosh.obj only.
12 *
13 * Function prefixes:
14 * -- dosh* Dos (Control Program) helper functions
15 *
16 * This has the same header as dosh.c, dosh.h.
17 *
18 * The partition functions in this file are based on
19 * code which has kindly been provided by Dmitry A. Steklenev.
20 * See doshGetPartitionsList for how to use these.
21 *
22 * Note: Version numbering in this file relates to XWorkplace version
23 * numbering.
24 *
25 *@@header "helpers\dosh.h"
26 *@@added V0.9.4 (2000-07-27) [umoeller]
27 */
28
29/*
30 * This file Copyright (C) 1997-2000 Ulrich M”ller,
31 * Dmitry A. Steklenev.
32 * This file is part of the "XWorkplace helpers" source package.
33 * This is free software; you can redistribute it and/or modify
34 * it under the terms of the GNU General Public License as published
35 * by the Free Software Foundation, in version 2 as it comes in the
36 * "COPYING" file of the XWorkplace main distribution.
37 * This program is distributed in the hope that it will be useful,
38 * but WITHOUT ANY WARRANTY; without even the implied warranty of
39 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40 * GNU General Public License for more details.
41 */
42
43#define OS2EMX_PLAIN_CHAR
44 // this is needed for "os2emx.h"; if this is defined,
45 // emx will define PSZ as _signed_ char, otherwise
46 // as unsigned char
47
48#define INCL_DOSMODULEMGR
49#define INCL_DOSPROCESS
50#define INCL_DOSSESMGR
51#define INCL_DOSQUEUES
52#define INCL_DOSMISC
53#define INCL_DOSDEVICES
54#define INCL_DOSDEVIOCTL
55#define INCL_DOSERRORS
56#include <os2.h>
57
58#include <stdlib.h>
59#include <string.h>
60#include <stdio.h>
61#include <ctype.h>
62
63#include "setup.h" // code generation and debugging options
64
65#include "helpers\dosh.h"
66#include "helpers\stringh.h"
67
68#pragma hdrstop
69
70/*
71 *@@category: Helpers\Control program helpers\Miscellaneous
72 */
73
74/* ******************************************************************
75 *
76 * Miscellaneous
77 *
78 ********************************************************************/
79
80/*
81 *@@ doshIsValidFileName:
82 * this returns NO_ERROR only if pszFile is a valid file name.
83 * This may include a full path.
84 *
85 * If a drive letter is specified, this checks for whether
86 * that drive is a FAT drive and adjust the checks accordingly,
87 * i.e. 8+3 syntax (per path component).
88 *
89 * If no drive letter is specified, this check is performed
90 * for the current drive.
91 *
92 * This also checks if pszFileNames contains characters which
93 * are invalid for the current drive.
94 *
95 * Note: this performs syntactic checks only. This does not
96 * check for whether the specified path components exist.
97 * However, it _is_ checked for whether the given drive
98 * exists.
99 *
100 * This func is especially useful to check filenames that
101 * have been entered by the user in a "Save as" dialog.
102 *
103 * If an error is found, the corresponding DOS error code
104 * is returned:
105 * -- ERROR_INVALID_DRIVE
106 * -- ERROR_FILENAME_EXCED_RANGE (on FAT: no 8+3 filename)
107 * -- ERROR_INVALID_NAME (invalid character)
108 * -- ERROR_CURRENT_DIRECTORY (if fFullyQualified: no full path specified)
109 *
110 *@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
111 */
112
113APIRET doshIsValidFileName(const char* pcszFile,
114 BOOL fFullyQualified) // in: if TRUE, pcszFile must be fully q'fied
115{
116 APIRET arc = NO_ERROR;
117 CHAR szPath[CCHMAXPATH+4] = " :";
118 CHAR szComponent[CCHMAXPATH];
119 PSZ p1, p2;
120 BOOL fIsFAT = FALSE;
121 PSZ pszInvalid;
122
123 if (fFullyQualified) // V0.9.2 (2000-03-11) [umoeller]
124 {
125 if ( (*(pcszFile + 1) != ':')
126 || (*(pcszFile + 2) != '\\')
127 )
128 arc = ERROR_CURRENT_DIRECTORY;
129 }
130
131 // check drive first
132 if (*(pcszFile + 1) == ':')
133 {
134 CHAR cDrive = toupper(*pcszFile);
135 double d;
136 // drive specified:
137 strcpy(szPath, pcszFile);
138 szPath[0] = toupper(*pcszFile);
139 arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
140 }
141 else
142 {
143 // no drive specified: take current
144 ULONG ulDriveNum = 0,
145 ulDriveMap = 0;
146 arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
147 szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
148 szPath[1] = ':';
149 strcpy(&szPath[2], pcszFile);
150 }
151
152 if (arc == NO_ERROR)
153 {
154 fIsFAT = doshIsFileOnFAT(szPath);
155
156 pszInvalid = (fIsFAT)
157 ? "<>|+=:;,\"/[] " // invalid characters in FAT
158 : "<>|:\"/"; // invalid characters in IFS's
159
160 // now separate path components
161 p1 = &szPath[2]; // advance past ':'
162
163 do {
164
165 if (*p1 == '\\')
166 p1++;
167
168 p2 = strchr(p1, '\\');
169 if (p2 == NULL)
170 p2 = p1 + strlen(p1);
171
172 if (p1 != p2)
173 {
174 LONG lDotOfs = -1,
175 lAfterDot = -1;
176 ULONG cbFile,
177 ul;
178 PSZ pSource = szComponent;
179
180 strncpy(szComponent, p1, p2-p1);
181 szComponent[p2-p1] = 0;
182 cbFile = strlen(szComponent);
183
184 // now check each path component
185 for (ul = 0; ul < cbFile; ul++)
186 {
187 if (fIsFAT)
188 {
189 // on FAT: only 8 characters allowed before dot
190 if (*pSource == '.')
191 {
192 lDotOfs = ul;
193 lAfterDot = 0;
194 if (ul > 7)
195 return (ERROR_FILENAME_EXCED_RANGE);
196 }
197 }
198 // and check for invalid characters
199 if (strchr(pszInvalid, *pSource) != NULL)
200 return (ERROR_INVALID_NAME);
201
202 pSource++;
203
204 // on FAT, allow only three chars after dot
205 if (fIsFAT)
206 if (lAfterDot != -1)
207 {
208 lAfterDot++;
209 if (lAfterDot > 3)
210 return (ERROR_FILENAME_EXCED_RANGE);
211 }
212 }
213
214 // we are still missing the case of a FAT file
215 // name without extension; if so, check whether
216 // the file stem is <= 8 chars
217 if (fIsFAT)
218 if (lDotOfs == -1) // dot not found:
219 if (cbFile > 8)
220 return (ERROR_FILENAME_EXCED_RANGE);
221 }
222
223 // go for next component
224 p1 = p2+1;
225 } while (*p2);
226 }
227
228 return (arc);
229}
230
231/*
232 *@@ doshMakeRealName:
233 * this copies pszSource to pszTarget, replacing
234 * all characters which are not supported by file
235 * systems with cReplace.
236 * pszTarget must be at least the same size as pszSource.
237 * If (fIsFAT), the file name will be made FAT-compliant (8+3).
238 * Returns TRUE if characters were replaced.
239 *
240 *@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
241 */
242
243BOOL doshMakeRealName(PSZ pszTarget, // out: new real name
244 PSZ pszSource, // in: filename to translate
245 CHAR cReplace, // in: replacement char for invalid
246 // characters (e.g. '!')
247 BOOL fIsFAT) // in: make-FAT-compatible flag
248{
249 ULONG ul,
250 cbSource = strlen(pszSource);
251 LONG lDotOfs = -1,
252 lAfterDot = -1;
253 BOOL brc = FALSE;
254 PSZ pSource = pszSource,
255 pTarget = pszTarget,
256 pszInvalid = (fIsFAT)
257 ? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
258 : "*<>|:\"/\\"; // invalid characters in IFS's
259
260 for (ul = 0; ul < cbSource; ul++)
261 {
262 if (fIsFAT)
263 {
264 // on FAT: truncate filename if neccessary
265 if (*pSource == '.')
266 {
267 lDotOfs = ul;
268 lAfterDot = 0;
269 if (ul > 7) {
270 // only 8 characters allowed before dot,
271 // so set target ptr to dot pos
272 pTarget = pszTarget+8;
273 }
274 }
275 }
276 // and replace invalid characters
277 if (strchr(pszInvalid, *pSource) == NULL)
278 *pTarget = *pSource;
279 else
280 {
281 *pTarget = cReplace;
282 brc = TRUE;
283 }
284 pTarget++;
285 pSource++;
286
287 // on FAT, allow only three chars after dot
288 if (fIsFAT)
289 if (lAfterDot != -1)
290 {
291 lAfterDot++;
292 if (lAfterDot > 3)
293 break;
294 }
295 }
296 *pTarget = '\0';
297
298 if (fIsFAT)
299 {
300 // we are still missing the case of a FAT file
301 // name without extension; if so, check whether
302 // the file stem is <= 8 chars
303 if (lDotOfs == -1) // dot not found:
304 if (cbSource > 8)
305 *(pszTarget+8) = 0; // truncate
306
307 // convert to upper case
308 strupr(pszTarget);
309 }
310
311 return (brc);
312}
313
314/*
315 *@@ doshSetCurrentDir:
316 * sets the current working directory
317 * to the given path.
318 *
319 * As opposed to DosSetCurrentDir, this
320 * one will change the current drive
321 * also, if one is specified.
322 */
323
324APIRET doshSetCurrentDir(const char *pcszDir)
325{
326 if (pcszDir)
327 {
328 if (*pcszDir != 0)
329 if (*(pcszDir+1) == ':')
330 {
331 // drive given:
332 CHAR cDrive = toupper(*(pcszDir));
333 APIRET arc;
334 // change drive
335 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
336 // 1 = A:, 2 = B:, ...
337 if (arc != NO_ERROR)
338 return (arc);
339 }
340
341 return (DosSetCurrentDir((PSZ)pcszDir));
342 }
343 return (ERROR_INVALID_PARAMETER);
344}
345
346/*
347 *@@category: Helpers\Control program helpers\Environment management
348 */
349
350/* ******************************************************************
351 *
352 * Environment helpers
353 *
354 ********************************************************************/
355
356/*
357 *@@ doshParseEnvironment:
358 * this takes one of those ugly environment strings
359 * as used by DosStartSession and WinStartApp (with
360 * lots of zero-terminated strings one after another
361 * and a duplicate zero byte as a terminator) as
362 * input and splits it into an array of separate
363 * strings in pEnv.
364 *
365 * The newly allocated strings are stored in in
366 * pEnv->papszVars. The array count is stored in
367 * pEnv->cVars.
368 *
369 * Each environment variable will be copied into
370 * one newly allocated string in the array. Use
371 * doshFreeEnvironment to free the memory allocated
372 * by this function.
373 *
374 * Use the following code to browse thru the array:
375 +
376 + DOSENVIRONMENT Env = {0};
377 + if (doshParseEnvironment(pszEnv,
378 + &Env)
379 + == NO_ERROR)
380 + {
381 + if (Env.papszVars)
382 + {
383 + PSZ *ppszThis = Env.papszVars;
384 + for (ul = 0;
385 + ul < Env.cVars;
386 + ul++)
387 + {
388 + PSZ pszThis = *ppszThis;
389 + // pszThis now has something like PATH=C:\TEMP
390 + // ...
391 + // next environment string
392 + ppszThis++;
393 + }
394 + }
395 + doshFreeEnvironment(&Env);
396 + }
397 *
398 *@@added V0.9.4 (2000-08-02) [umoeller]
399 */
400
401APIRET doshParseEnvironment(const char *pcszEnv,
402 PDOSENVIRONMENT pEnv)
403{
404 APIRET arc = NO_ERROR;
405 if (!pcszEnv)
406 arc = ERROR_INVALID_PARAMETER;
407 else
408 {
409 PSZ pszVarThis = (PSZ)pcszEnv;
410 ULONG cVars = 0;
411 // count strings
412 while (*pszVarThis)
413 {
414 cVars++;
415 pszVarThis += strlen(pszVarThis) + 1;
416 }
417
418 pEnv->cVars = cVars;
419 pEnv->papszVars = 0;
420
421 if (cVars)
422 {
423 PSZ *papsz = (PSZ*)malloc(sizeof(PSZ) * cVars);
424 if (!papsz)
425 arc = ERROR_NOT_ENOUGH_MEMORY;
426 else
427 {
428 PSZ *ppszTarget = papsz;
429 memset(papsz, 0, sizeof(PSZ) * cVars);
430 pszVarThis = (PSZ)pcszEnv;
431 while (*pszVarThis)
432 {
433 *ppszTarget = strdup(pszVarThis);
434 ppszTarget++;
435 pszVarThis += strlen(pszVarThis) + 1;
436 }
437
438 pEnv->papszVars = papsz;
439 }
440 }
441 }
442
443 return (arc);
444}
445
446/*
447 *@@ doshGetEnvironment:
448 * calls doshParseEnvironment for the current
449 * process environment, which is retrieved from
450 * the info blocks.
451 *
452 *@@added V0.9.4 (2000-07-19) [umoeller]
453 */
454
455APIRET doshGetEnvironment(PDOSENVIRONMENT pEnv)
456{
457 APIRET arc = NO_ERROR;
458 if (!pEnv)
459 arc = ERROR_INVALID_PARAMETER;
460 else
461 {
462 PTIB ptib = 0;
463 PPIB ppib = 0;
464 arc = DosGetInfoBlocks(&ptib, &ppib);
465 if (arc == NO_ERROR)
466 {
467 PSZ pszEnv = ppib->pib_pchenv;
468 if (pszEnv)
469 arc = doshParseEnvironment(pszEnv, pEnv);
470 else
471 arc = ERROR_BAD_ENVIRONMENT;
472 }
473 }
474
475 return (arc);
476}
477
478/*
479 *@@ doshFindEnvironmentVar:
480 * returns the PSZ* in the pEnv->papszVars array
481 * which specifies the environment variable in pszVarName.
482 *
483 * With pszVarName, you can either specify the variable
484 * name only ("VARNAME") or a full environment string
485 * ("VARNAME=BLAH"). In any case, only the variable name
486 * is compared.
487 *
488 * Returns NULL if no such variable name was found in
489 * the array.
490 *
491 *@@added V0.9.4 (2000-07-19) [umoeller]
492 */
493
494PSZ* doshFindEnvironmentVar(PDOSENVIRONMENT pEnv,
495 PSZ pszVarName)
496{
497 PSZ *ppszRet = 0;
498 if (pEnv)
499 {
500 if ((pEnv->papszVars) && (pszVarName))
501 {
502 PSZ *ppszThis = pEnv->papszVars;
503 PSZ pszThis;
504 ULONG ul = 0;
505 ULONG ulVarNameLen = 0;
506
507 PSZ pszSearch = NULL; // receives "VARNAME="
508 PSZ pFirstEqual = strchr(pszVarName, '=');
509 if (pFirstEqual)
510 pszSearch = strhSubstr(pszVarName, pFirstEqual + 1);
511 else
512 {
513 ulVarNameLen = strlen(pszVarName);
514 pszSearch = (PSZ)malloc(ulVarNameLen + 2);
515 memcpy(pszSearch, pszVarName, ulVarNameLen);
516 *(pszSearch + ulVarNameLen) = '=';
517 *(pszSearch + ulVarNameLen + 1) = 0;
518 }
519
520 ulVarNameLen = strlen(pszSearch);
521
522 for (ul = 0;
523 ul < pEnv->cVars;
524 ul++)
525 {
526 pszThis = *ppszThis;
527
528 if (strnicmp(*ppszThis, pszSearch, ulVarNameLen) == 0)
529 {
530 ppszRet = ppszThis;
531 break;
532 }
533
534 // next environment string
535 ppszThis++;
536 }
537 }
538 }
539
540 return (ppszRet);
541}
542
543/*
544 *@@ doshSetEnvironmentVar:
545 * sets an environment variable in the specified
546 * environment, which must have been initialized
547 * using doshGetEnvironment first.
548 *
549 * pszNewEnv must be a full environment string
550 * in the form "VARNAME=VALUE".
551 *
552 * If "VARNAME" has already been set to something
553 * in the string array in pEnv, that array item
554 * is replaced. Otherwise a new item is added to
555 * the array, and pEnv->cVars is raised by one.
556 *
557 *@@added V0.9.4 (2000-07-19) [umoeller]
558 */
559
560APIRET doshSetEnvironmentVar(PDOSENVIRONMENT pEnv,
561 PSZ pszNewEnv)
562{
563 APIRET arc = NO_ERROR;
564 if (!pEnv)
565 arc = ERROR_INVALID_PARAMETER;
566 else
567 {
568 if ((!pEnv->papszVars) || (!pszNewEnv))
569 arc = ERROR_INVALID_PARAMETER;
570 else
571 {
572 PSZ *ppszEnvLine = doshFindEnvironmentVar(pEnv, pszNewEnv);
573 if (ppszEnvLine)
574 {
575 // was set already: replace
576 free(*ppszEnvLine);
577 *ppszEnvLine = strdup(pszNewEnv);
578 }
579 else
580 {
581 PSZ *ppszNew;
582 PSZ *papszNew;
583 // not set already:
584 // append
585 // reallocate array and add new string
586 papszNew = (PSZ*)realloc(pEnv->papszVars, sizeof(PSZ) * (pEnv->cVars + 1));
587 if (!papszNew)
588 arc = ERROR_NOT_ENOUGH_MEMORY;
589 else
590 {
591 pEnv->papszVars = papszNew;
592 ppszNew = pEnv->papszVars + pEnv->cVars;
593 pEnv->cVars++;
594 *ppszNew = strdup(pszNewEnv);
595 }
596 }
597 }
598 }
599
600 return (arc);
601}
602
603/*
604 *@@ doshConvertEnvironment:
605 * converts an environment initialized by doshGetEnvironment
606 * to the string format required by WinStartApp and DosExecPgm,
607 * that is, one memory block is allocated in *ppszEnv and all
608 * strings in pEnv->papszVars are copied to that block. Each
609 * string is terminated with a null character; the last string
610 * is terminated with two null characters.
611 *
612 * Use free() to free the memory block allocated by this
613 * function in *ppszEnv.
614 *
615 *@@added V0.9.4 (2000-07-19) [umoeller]
616 */
617
618APIRET doshConvertEnvironment(PDOSENVIRONMENT pEnv,
619 PSZ *ppszEnv, // out: environment string
620 PULONG pulSize) // out: size of block allocated in *ppszEnv
621{
622 APIRET arc = NO_ERROR;
623 if (!pEnv)
624 arc = ERROR_INVALID_PARAMETER;
625 else
626 {
627 if (!pEnv->papszVars)
628 arc = ERROR_INVALID_PARAMETER;
629 else
630 {
631 // count memory needed for all strings
632 ULONG cbNeeded = 0,
633 ul = 0;
634 PSZ *ppszThis = pEnv->papszVars;
635
636 for (ul = 0;
637 ul < pEnv->cVars;
638 ul++)
639 {
640 cbNeeded += strlen(*ppszThis) + 1; // length of string plus null terminator
641
642 // next environment string
643 ppszThis++;
644 }
645
646 cbNeeded++; // for another null terminator
647
648 *ppszEnv = (PSZ)malloc(cbNeeded);
649 if (!(*ppszEnv))
650 arc = ERROR_NOT_ENOUGH_MEMORY;
651 else
652 {
653 PSZ pTarget = *ppszEnv;
654 *pulSize = cbNeeded;
655 ppszThis = pEnv->papszVars;
656
657 // now copy each string
658 for (ul = 0;
659 ul < pEnv->cVars;
660 ul++)
661 {
662 PSZ pSource = *ppszThis;
663
664 while ((*pTarget++ = *pSource++))
665 ;
666
667 // *pTarget++ = 0; // append null terminator per string
668
669 // next environment string
670 ppszThis++;
671 }
672
673 *pTarget++ = 0; // append second null terminator
674 }
675 }
676 }
677
678 return (arc);
679}
680
681/*
682 *@@ doshFreeEnvironment:
683 * frees memory allocated by doshGetEnvironment.
684 *
685 *@@added V0.9.4 (2000-07-19) [umoeller]
686 */
687
688APIRET doshFreeEnvironment(PDOSENVIRONMENT pEnv)
689{
690 APIRET arc = NO_ERROR;
691 if (!pEnv)
692 arc = ERROR_INVALID_PARAMETER;
693 else
694 {
695 if (!pEnv->papszVars)
696 arc = ERROR_INVALID_PARAMETER;
697 else
698 {
699 PSZ *ppszThis = pEnv->papszVars;
700 PSZ pszThis;
701 ULONG ul = 0;
702
703 for (ul = 0;
704 ul < pEnv->cVars;
705 ul++)
706 {
707 pszThis = *ppszThis;
708 free(pszThis);
709 // *ppszThis = NULL;
710 // next environment string
711 ppszThis++;
712 }
713
714 free(pEnv->papszVars);
715 pEnv->cVars = 0;
716 }
717 }
718
719 return (arc);
720}
721
722/*
723 *@@category: Helpers\Control program helpers\Module handling
724 */
725
726/* ******************************************************************
727 *
728 * Module handling helpers
729 *
730 ********************************************************************/
731
732/*
733 *@@ doshResolveImports:
734 * this function loads the module called pszModuleName
735 * and resolves imports dynamically using DosQueryProcAddress.
736 *
737 * To specify the functions to be imported, a RESOLVEFUNCTION
738 * array is used. In each of the array items, specify the
739 * name of the function and a pointer to a function pointer
740 * where to store the resolved address.
741 *
742 *@@added V0.9.3 (2000-04-29) [umoeller]
743 */
744
745APIRET doshResolveImports(PSZ pszModuleName, // in: DLL to load
746 HMODULE *phmod, // out: module handle
747 PRESOLVEFUNCTION paResolves, // in/out: function resolves
748 ULONG cResolves) // in: array item count (not array size!)
749{
750 CHAR szName[CCHMAXPATH];
751 APIRET arc = DosLoadModule(szName,
752 sizeof(szName),
753 pszModuleName,
754 phmod);
755 if (arc == NO_ERROR)
756 {
757 ULONG ul;
758 for (ul = 0;
759 ul < cResolves;
760 ul++)
761 {
762 arc = DosQueryProcAddr(*phmod,
763 0, // ordinal, ignored
764 (PSZ)paResolves[ul].pcszFunctionName,
765 paResolves[ul].ppFuncAddress);
766
767 /* _Pmpf(("Resolved %s to 0x%lX, rc: %d",
768 paResolves[ul].pcszFunctionName,
769 *paResolves[ul].ppFuncAddress,
770 arc)); */
771 if (arc != NO_ERROR)
772 break;
773 }
774 }
775
776 return (arc);
777}
778
779/*
780 *@@category: Helpers\Control program helpers\Executable info
781 */
782
783/********************************************************************
784 *
785 * Executable functions
786 *
787 ********************************************************************/
788
789/*
790 *@@ doshExecOpen:
791 * this opens the specified executable file
792 * (which can be an .EXE, .COM, .DLL, or
793 * driver file) for use with the other
794 * doshExec* functions.
795 *
796 * If no error occurs, NO_ERROR is returned
797 * and a pointer to a new EXECUTABLE structure
798 * is stored in *ppExec. Consider this pointer a
799 * handle and pass it to doshExecClose when the
800 * executable is no longer needed to free
801 * resources.
802 *
803 * If NO_ERROR is returned, all the fields through
804 * ulOS are set in EXECUTABLE. The psz* fields
805 * which follow afterwards require an additional
806 * call to doshExecQueryBldLevel.
807 *
808 * If errors occur, this function returns the
809 * following error codes:
810 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
811 * -- ERROR_INVALID_EXE_SIGNATURE: specified file
812 * has no DOS EXE header.
813 * -- ERROR_INVALID_PARAMETER: ppExec is NULL.
814 *
815 * plus those of DosOpen, DosSetFilePtr, and
816 * DosRead.
817 *
818 *@@added V0.9.0 [umoeller]
819 *@@changed V0.9.1 (2000-02-13) [umoeller]: fixed 32-bits flag
820 */
821
822APIRET doshExecOpen(const char* pcszExecutable,
823 PEXECUTABLE* ppExec)
824{
825 ULONG ulAction = 0;
826 HFILE hFile;
827 APIRET arc = DosOpen((PSZ)pcszExecutable,
828 &hFile,
829 &ulAction, // out: action taken
830 0, // in: new file (ignored for read-mode)
831 0, // in: new file attribs (ignored)
832 // open-flags
833 OPEN_ACTION_FAIL_IF_NEW
834 | OPEN_ACTION_OPEN_IF_EXISTS,
835 // open-mode
836 OPEN_FLAGS_FAIL_ON_ERROR // report errors to caller
837 | OPEN_FLAGS_SEQUENTIAL
838 | OPEN_FLAGS_NOINHERIT
839 | OPEN_SHARE_DENYNONE
840 | OPEN_ACCESS_READONLY, // read-only mode
841 NULL); // no EAs
842
843 if (arc == NO_ERROR)
844 {
845 // file opened successfully:
846 // create EXECUTABLE structure
847
848 if (ppExec)
849 {
850 *ppExec = (PEXECUTABLE)malloc(sizeof(EXECUTABLE));
851 if (*ppExec)
852 {
853 ULONG ulLocal = 0;
854
855 memset((*ppExec), 0, sizeof(EXECUTABLE));
856
857 // read old DOS EXE header
858 (*ppExec)->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
859 if ((*ppExec)->pDosExeHeader == NULL)
860 arc = ERROR_NOT_ENOUGH_MEMORY;
861 else
862 {
863 ULONG ulBytesRead = 0;
864 arc = DosSetFilePtr(hFile,
865 0L,
866 FILE_BEGIN,
867 &ulLocal); // out: new offset
868 arc = DosRead(hFile,
869 (*ppExec)->pDosExeHeader,
870 sizeof(DOSEXEHEADER),
871 &((*ppExec)->cbDosExeHeader));
872 // now check if we really have a DOS header
873 if ((*ppExec)->pDosExeHeader->usDosExeID != 0x5a4d)
874 arc = ERROR_INVALID_EXE_SIGNATURE;
875 else
876 {
877 // we have a DOS header:
878 if ((*ppExec)->pDosExeHeader->usRelocTableOfs < 0x40)
879 {
880 // not LX or PE or NE:
881 (*ppExec)->ulOS = EXEOS_DOS3;
882 (*ppExec)->ulExeFormat = EXEFORMAT_OLDDOS;
883 }
884 else
885 {
886 // either LX or PE or NE:
887 // read more bytes from position
888 // specified in header
889 arc = DosSetFilePtr(hFile,
890 (*ppExec)->pDosExeHeader->usNewHeaderOfs,
891 FILE_BEGIN,
892 &ulLocal);
893
894 if (arc == NO_ERROR)
895 {
896 PBYTE pbCheckOS = NULL;
897
898 // read two chars to find out header type
899 CHAR achNewHeaderType[2] = "";
900 arc = DosRead(hFile,
901 &achNewHeaderType,
902 sizeof(achNewHeaderType),
903 &ulBytesRead);
904 // reset file ptr
905 DosSetFilePtr(hFile,
906 (*ppExec)->pDosExeHeader->usNewHeaderOfs,
907 FILE_BEGIN,
908 &ulLocal);
909
910 if (memcmp(achNewHeaderType, "NE", 2) == 0)
911 {
912 // New Executable:
913 (*ppExec)->ulExeFormat = EXEFORMAT_NE;
914 // read NE header
915 (*ppExec)->pNEHeader = (PNEHEADER)malloc(sizeof(NEHEADER));
916 DosRead(hFile,
917 (*ppExec)->pNEHeader,
918 sizeof(NEHEADER),
919 &((*ppExec)->cbNEHeader));
920 if ((*ppExec)->cbNEHeader == sizeof(NEHEADER))
921 pbCheckOS = &((*ppExec)->pNEHeader->bTargetOS);
922 }
923 else if ( (memcmp(achNewHeaderType, "LX", 2) == 0)
924 || (memcmp(achNewHeaderType, "LE", 2) == 0)
925 // this is used by SMARTDRV.EXE
926 )
927 {
928 // OS/2 Linear Executable:
929 (*ppExec)->ulExeFormat = EXEFORMAT_LX;
930 // read LX header
931 (*ppExec)->pLXHeader = (PLXHEADER)malloc(sizeof(LXHEADER));
932 DosRead(hFile,
933 (*ppExec)->pLXHeader,
934 sizeof(LXHEADER),
935 &((*ppExec)->cbLXHeader));
936 if ((*ppExec)->cbLXHeader == sizeof(LXHEADER))
937 pbCheckOS = (PBYTE)(&((*ppExec)->pLXHeader->usTargetOS));
938 }
939 else if (memcmp(achNewHeaderType, "PE", 2) == 0)
940 {
941 (*ppExec)->ulExeFormat = EXEFORMAT_PE;
942 (*ppExec)->ulOS = EXEOS_WIN32;
943 (*ppExec)->f32Bits = TRUE;
944 }
945 else
946 arc = ERROR_INVALID_EXE_SIGNATURE;
947
948 if (pbCheckOS)
949 // BYTE to check for operating system
950 // (NE and LX):
951 switch (*pbCheckOS)
952 {
953 case NEOS_OS2:
954 (*ppExec)->ulOS = EXEOS_OS2;
955 if ((*ppExec)->ulExeFormat == EXEFORMAT_LX)
956 (*ppExec)->f32Bits = TRUE;
957 break;
958 case NEOS_WIN16:
959 (*ppExec)->ulOS = EXEOS_WIN16;
960 break;
961 case NEOS_DOS4:
962 (*ppExec)->ulOS = EXEOS_DOS4;
963 break;
964 case NEOS_WIN386:
965 (*ppExec)->ulOS = EXEOS_WIN386;
966 (*ppExec)->f32Bits = TRUE;
967 break;
968 }
969 }
970 }
971 }
972
973 // store exec's HFILE
974 (*ppExec)->hfExe = hFile;
975 }
976
977 if (arc != NO_ERROR)
978 // error: clean up
979 doshExecClose(*ppExec);
980
981 } // end if (*ppExec)
982 else
983 arc = ERROR_NOT_ENOUGH_MEMORY;
984 } // end if (ppExec)
985 else
986 arc = ERROR_INVALID_PARAMETER;
987 } // end if (arc == NO_ERROR)
988
989 return (arc);
990}
991
992/*
993 *@@ doshExecClose:
994 * this closes an executable opened with doshExecOpen.
995 * Always call this function if NO_ERROR was returned by
996 * doshExecOpen.
997 *
998 *@@added V0.9.0 [umoeller]
999 */
1000
1001APIRET doshExecClose(PEXECUTABLE pExec)
1002{
1003 APIRET arc = NO_ERROR;
1004 if (pExec)
1005 {
1006 if (pExec->pDosExeHeader)
1007 free(pExec->pDosExeHeader);
1008 if (pExec->pNEHeader)
1009 free(pExec->pNEHeader);
1010 if (pExec->pLXHeader)
1011 free(pExec->pLXHeader);
1012
1013 if (pExec->pszDescription)
1014 free(pExec->pszDescription);
1015 if (pExec->pszVendor)
1016 free(pExec->pszVendor);
1017 if (pExec->pszVersion)
1018 free(pExec->pszVersion);
1019 if (pExec->pszInfo)
1020 free(pExec->pszInfo);
1021
1022 DosClose(pExec->hfExe);
1023
1024 free(pExec);
1025 }
1026 else
1027 arc = ERROR_INVALID_PARAMETER;
1028
1029 return (arc);
1030}
1031
1032/*
1033 *@@ doshExecQueryBldLevel:
1034 * this retrieves buildlevel information for an
1035 * LX or NE executable previously opened with
1036 * doshExecOpen.
1037 *
1038 * BuildLevel information must be contained in the
1039 * DESCRIPTION field of an executable's module
1040 * definition (.DEF) file. In order to be readable
1041 * by BLDLEVEL.EXE (which ships with OS/2), this
1042 * string must have the following format:
1043 *
1044 + Description '@#AUTHOR:VERSION#@ DESCRIPTION'
1045 *
1046 * Example:
1047 *
1048 + Description '@#Ulrich M”ller:0.9.0#@ XWorkplace Sound Support Module'
1049 *
1050 * The "Description" entry always ends up as the
1051 * very first entry in the non-resident name table
1052 * in LX and NE executables. So this is what we retrieve
1053 * here.
1054 *
1055 * If the first entry in that table exists, NO_ERROR is
1056 * returned and at least the pszDescription field in
1057 * EXECUTABLE is set to that information.
1058 *
1059 * If that string is in IBM BLDLEVEL format, the string
1060 * is automatically parsed, and the pszVendor, pszVersion,
1061 * and pszInfo fields are also set. In the above examples,
1062 * this would return the following information:
1063 + pszVendor = "Ulrich M”ller"
1064 + pszVersion = "0.9.0"
1065 + pszInfo = "XWorkplace Sound Support Module"
1066 *
1067 * If that string is not in BLDLEVEL format, only pszDescription
1068 * will be set. The other fields remain NULL.
1069 *
1070 * This returns the following errors:
1071 * -- ERROR_INVALID_PARAMETER: pExec invalid
1072 * -- ERROR_INVALID_EXE_SIGNATURE (191): pExec is not in LX or NE format
1073 * -- ERROR_INVALID_DATA (13): non-resident name table not found.
1074 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
1075 *
1076 * plus the error codes of DosSetFilePtr and DosRead.
1077 *
1078 *@@added V0.9.0 [umoeller]
1079 *@@changed V0.9.0 (99-10-22) [umoeller]: NE format now supported
1080 *@@changed V0.9.1 (99-12-06): fixed memory leak
1081 */
1082
1083APIRET doshExecQueryBldLevel(PEXECUTABLE pExec)
1084{
1085 APIRET arc = NO_ERROR;
1086 PSZ pszNameTable = NULL;
1087 ULONG ulNRNTOfs = 0;
1088
1089 do
1090 {
1091 ULONG ulLocal = 0,
1092 ulBytesRead = 0;
1093 PSZ pStartOfAuthor = NULL;
1094
1095 if (pExec == NULL)
1096 {
1097 arc = ERROR_INVALID_PARAMETER;
1098 break;
1099 }
1100
1101 if (pExec->ulExeFormat == EXEFORMAT_LX)
1102 {
1103 // OK, LX format:
1104 // check if we have a non-resident name table
1105 if (pExec->pLXHeader == NULL)
1106 {
1107 arc = ERROR_INVALID_DATA;
1108 break;
1109 }
1110 if (pExec->pLXHeader->ulNonResdNameTblOfs == 0)
1111 {
1112 arc = ERROR_INVALID_DATA;
1113 break;
1114 }
1115
1116 ulNRNTOfs = pExec->pLXHeader->ulNonResdNameTblOfs;
1117 }
1118 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1119 {
1120 // OK, NE format:
1121 // check if we have a non-resident name table
1122 if (pExec->pNEHeader == NULL)
1123 {
1124 arc = ERROR_INVALID_DATA;
1125 break;
1126 }
1127 if (pExec->pNEHeader->ulNonResdTblOfs == 0)
1128 {
1129 arc = ERROR_INVALID_DATA;
1130 break;
1131 }
1132
1133 ulNRNTOfs = pExec->pNEHeader->ulNonResdTblOfs;
1134 }
1135 else
1136 {
1137 // neither LX nor NE: stop
1138 arc = ERROR_INVALID_EXE_SIGNATURE;
1139 break;
1140 }
1141
1142 if (ulNRNTOfs == 0)
1143 {
1144 // shouldn't happen
1145 arc = ERROR_INVALID_DATA;
1146 break;
1147 }
1148
1149 // move EXE file pointer to offset of non-resident name table
1150 // (from LX header)
1151 arc = DosSetFilePtr(pExec->hfExe, // file is still open
1152 ulNRNTOfs, // ofs determined above
1153 FILE_BEGIN,
1154 &ulLocal);
1155 if (arc != NO_ERROR)
1156 break;
1157
1158 // allocate memory as necessary
1159 pszNameTable = (PSZ)malloc(2001); // should suffice, because each entry
1160 // may only be 255 bytes in length
1161 if (pszNameTable)
1162 {
1163 arc = DosRead(pExec->hfExe,
1164 pszNameTable,
1165 2000,
1166 &ulBytesRead);
1167 if (arc != NO_ERROR)
1168 break;
1169 if (*pszNameTable == 0)
1170 {
1171 // first byte (length byte) is null:
1172 arc = ERROR_INVALID_DATA;
1173 free (pszNameTable); // fixed V0.9.1 (99-12-06)
1174 break;
1175 }
1176
1177 // now copy the string, which is in Pascal format
1178 pExec->pszDescription = (PSZ)malloc((*pszNameTable) + 1); // addt'l null byte
1179 memcpy(pExec->pszDescription,
1180 pszNameTable + 1, // skip length byte
1181 *pszNameTable); // length byte
1182 // terminate string
1183 *(pExec->pszDescription + (*pszNameTable)) = 0;
1184
1185 // _Pmpf(("pszDescription: %s", pExec->pszDescription));
1186
1187 // now parse the damn thing:
1188 // @#AUTHOR:VERSION#@ DESCRIPTION
1189 // but skip the first byte, which has the string length
1190 pStartOfAuthor = strstr(pExec->pszDescription, "@#");
1191 if (pStartOfAuthor)
1192 {
1193 PSZ pStartOfInfo = strstr(pStartOfAuthor + 2, "#@");
1194 // _Pmpf(("Testing"));
1195 if (pStartOfInfo)
1196 {
1197 PSZ pEndOfAuthor = strchr(pStartOfAuthor + 2, ':');
1198 // _Pmpf(("pStartOfinfo: %s", pStartOfInfo));
1199 if (pEndOfAuthor)
1200 {
1201 // _Pmpf(("pEndOfAuthor: %s", pEndOfAuthor));
1202 pExec->pszVendor = strhSubstr(pStartOfAuthor + 2, pEndOfAuthor);
1203 pExec->pszVersion = strhSubstr(pEndOfAuthor + 1, pStartOfInfo);
1204 // skip "@#" in info string
1205 pStartOfInfo += 2;
1206 // skip leading spaces in info string
1207 while (*pStartOfInfo == ' ')
1208 pStartOfInfo++;
1209 // and copy until end of string
1210 pExec->pszInfo = strdup(pStartOfInfo);
1211 }
1212 }
1213 }
1214
1215 free(pszNameTable);
1216 }
1217 else
1218 arc = ERROR_NOT_ENOUGH_MEMORY;
1219 } while (FALSE);
1220
1221
1222 return (arc);
1223}
1224
1225/*
1226 *@@category: Helpers\Control program helpers\Partitions info
1227 */
1228
1229/********************************************************************
1230 *
1231 * Partition functions
1232 *
1233 ********************************************************************/
1234
1235/*
1236 *@@ doshQueryDiskCount:
1237 * returns the no. of physical disks installed
1238 * on the system.
1239 *
1240 * Based on code (C) Dmitry A. Steklenev.
1241 *
1242 *@@added V0.9.0 [umoeller]
1243 */
1244
1245UINT doshQueryDiskCount(VOID)
1246{
1247 USHORT count = 0;
1248
1249 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &count, 2, 0, 0);
1250 return (count);
1251}
1252
1253/*
1254 *@@ doshReadSector:
1255 * reads a physical disk sector.
1256 *
1257 * If NO_ERROR is returned, the sector contents
1258 * have been stored in *buff.
1259 *
1260 * Based on code (C) Dmitry A. Steklenev.
1261 *
1262 *@@added V0.9.0 [umoeller]
1263 */
1264
1265APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
1266 void *buff,
1267 USHORT head,
1268 USHORT cylinder,
1269 USHORT sector)
1270{
1271 UINT arc;
1272 HFILE dh = 0;
1273 char dn[256];
1274 // char ms[256];
1275
1276 sprintf( dn, "%u:", disk );
1277 arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3);
1278
1279 if (arc)
1280 // error:
1281 return (arc);
1282 else
1283 {
1284 TRACKLAYOUT DiskIOParm;
1285 ULONG IOCtlDataLength = sizeof(DiskIOParm);
1286 ULONG IOCtlParmLength = 512;
1287
1288 DiskIOParm.bCommand = 0;
1289 DiskIOParm.usHead = head;
1290 DiskIOParm.usCylinder = cylinder;
1291 DiskIOParm.usFirstSector = 0;
1292 DiskIOParm.cSectors = 1;
1293 DiskIOParm.TrackTable[0].usSectorNumber = sector;
1294 DiskIOParm.TrackTable[0].usSectorSize = 512;
1295
1296 arc = DosDevIOCtl(dh,
1297 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
1298 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
1299 buff , IOCtlDataLength, &IOCtlDataLength);
1300
1301 if(arc)
1302 {
1303 // error:
1304 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
1305 return (arc);
1306 }
1307
1308 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
1309 }
1310 return (NO_ERROR);
1311}
1312
1313/*
1314 *@@ doshType2FSName:
1315 * this returns a static, zero-terminated string
1316 * for the given FS type. This is always 7 bytes
1317 * in length.
1318 *
1319 * Values for operating system indicator:
1320 * -- 00h empty
1321 * -- 01h DOS 12-bit FAT
1322 * -- 02h XENIX root file system
1323 * -- 03h XENIX /usr file system (obsolete)
1324 * -- 04h DOS 16-bit FAT (up to 32M)
1325 * -- 05h DOS 3.3+ extended partition
1326 * -- 06h DOS 3.31+ Large File System (16-bit FAT, over 32M)
1327 * -- 07h QNX
1328 * -- 07h OS/2 HPFS
1329 * -- 07h Windows NT NTFS
1330 * -- 07h Advanced Unix
1331 * -- 08h OS/2 (v1.0-1.3 only)
1332 * -- 08h AIX bootable partition, SplitDrive
1333 * -- 08h Commodore DOS
1334 * -- 08h DELL partition spanning multiple drives
1335 * -- 09h AIX data partition
1336 * -- 09h Coherent filesystem
1337 * -- 0Ah OS/2 Boot Manager
1338 * -- 0Ah OPUS
1339 * -- 0Ah Coherent swap partition
1340 * -- 0Bh Windows95 with 32-bit FAT
1341 * -- 0Ch Windows95 with 32-bit FAT (using LBA-mode INT 13 extensions)
1342 * -- 0Eh logical-block-addressable VFAT (same as 06h but using LBA-mode INT 13)
1343 * -- 0Fh logical-block-addressable VFAT (same as 05h but using LBA-mode INT 13)
1344 * -- 10h OPUS
1345 * -- 11h OS/2 Boot Manager hidden 12-bit FAT partition
1346 * -- 12h Compaq Diagnostics partition
1347 * -- 14h (resulted from using Novell DOS 7.0 FDISK to delete Linux Native part)
1348 * -- 14h OS/2 Boot Manager hidden sub-32M 16-bit FAT partition
1349 * -- 16h OS/2 Boot Manager hidden over-32M 16-bit FAT partition
1350 * -- 17h OS/2 Boot Manager hidden HPFS partition
1351 * -- 18h AST special Windows swap file ("Zero-Volt Suspend" partition)
1352 * -- 21h officially listed as reserved
1353 * -- 23h officially listed as reserved
1354 * -- 24h NEC MS-DOS 3.x
1355 * -- 26h officially listed as reserved
1356 * -- 31h officially listed as reserved
1357 * -- 33h officially listed as reserved
1358 * -- 34h officially listed as reserved
1359 * -- 36h officially listed as reserved
1360 * -- 38h Theos
1361 * -- 3Ch PowerQuest PartitionMagic recovery partition
1362 * -- 40h VENIX 80286
1363 * -- 41h Personal RISC Boot
1364 * -- 42h SFS (Secure File System) by Peter Gutmann
1365 * -- 50h OnTrack Disk Manager, read-only partition
1366 * -- 51h OnTrack Disk Manager, read/write partition
1367 * -- 51h NOVEL
1368 * -- 52h CP/M
1369 * -- 52h Microport System V/386
1370 * -- 53h OnTrack Disk Manager, write-only partition???
1371 * -- 54h OnTrack Disk Manager (DDO)
1372 * -- 56h GoldenBow VFeature
1373 * -- 61h SpeedStor
1374 * -- 63h Unix SysV/386, 386/ix
1375 * -- 63h Mach, MtXinu BSD 4.3 on Mach
1376 * -- 63h GNU HURD
1377 * -- 64h Novell NetWare 286
1378 * -- 65h Novell NetWare (3.11)
1379 * -- 67h Novell
1380 * -- 68h Novell
1381 * -- 69h Novell
1382 * -- 70h DiskSecure Multi-Boot
1383 * -- 71h officially listed as reserved
1384 * -- 73h officially listed as reserved
1385 * -- 74h officially listed as reserved
1386 * -- 75h PC/IX
1387 * -- 76h officially listed as reserved
1388 * -- 80h Minix v1.1 - 1.4a
1389 * -- 81h Minix v1.4b+
1390 * -- 81h Linux
1391 * -- 81h Mitac Advanced Disk Manager
1392 * -- 82h Linux Swap partition
1393 * -- 82h Prime
1394 * -- 83h Linux native file system (ext2fs/xiafs)
1395 * -- 84h OS/2-renumbered type 04h partition (related to hiding DOS C: drive)
1396 * -- 86h FAT16 volume/stripe set (Windows NT)
1397 * -- 87h HPFS Fault-Tolerant mirrored partition
1398 * -- 87h NTFS volume/stripe set
1399 * -- 93h Amoeba file system
1400 * -- 94h Amoeba bad block table
1401 * -- A0h Phoenix NoteBIOS Power Management "Save-to-Disk" partition
1402 * -- A1h officially listed as reserved
1403 * -- A3h officially listed as reserved
1404 * -- A4h officially listed as reserved
1405 * -- A5h FreeBSD, BSD/386
1406 * -- A6h officially listed as reserved
1407 * -- B1h officially listed as reserved
1408 * -- B3h officially listed as reserved
1409 * -- B4h officially listed as reserved
1410 * -- B6h officially listed as reserved
1411 * -- B7h BSDI file system (secondarily swap)
1412 * -- B8h BSDI swap partition (secondarily file system)
1413 * -- C1h DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT partition
1414 * -- C4h DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT partition
1415 * -- C6h DR DOS 6.0 LOGIN.EXE-secured Huge partition
1416 * -- C6h corrupted FAT16 volume/stripe set (Windows NT)
1417 * -- C7h Syrinx Boot
1418 * -- C7h corrupted NTFS volume/stripe set
1419 * -- D8h CP/M-86
1420 * -- DBh CP/M, Concurrent CP/M, Concurrent DOS
1421 * -- DBh CTOS (Convergent Technologies OS)
1422 * -- E1h SpeedStor 12-bit FAT extended partition
1423 * -- E3h DOS read-only
1424 * -- E3h Storage Dimensions
1425 * -- E4h SpeedStor 16-bit FAT extended partition
1426 * -- E5h officially listed as reserved
1427 * -- E6h officially listed as reserved
1428 * -- F1h Storage Dimensions
1429 * -- F2h DOS 3.3+ secondary partition
1430 * -- F3h officially listed as reserved
1431 * -- F4h SpeedStor
1432 * -- F4h Storage Dimensions
1433 * -- F6h officially listed as reserved
1434 * -- FEh LANstep
1435 * -- FEh IBM PS/2 IML
1436 * -- FFh Xenix bad block table
1437 *
1438 * Note: for partition type 07h, one should inspect the partition boot record
1439 * for the actual file system type
1440 *
1441 * Based on code (C) Dmitry A. Steklenev.
1442 *
1443 *@@added V0.9.0 [umoeller]
1444 */
1445
1446char* doshType2FSName(unsigned char bFSType) // in: FS type
1447{
1448 PSZ zFSName = NULL;
1449
1450 switch (bFSType)
1451 {
1452 case PAR_UNUSED:
1453 zFSName = "UNUSED ";
1454 break;
1455 case PAR_FAT12SMALL:
1456 zFSName = "FAT-12 ";
1457 break;
1458 case PAR_XENIXROOT:
1459 zFSName = "XENIX ";
1460 break;
1461 case PAR_XENIXUSER:
1462 zFSName = "XENIX ";
1463 break;
1464 case PAR_FAT16SMALL:
1465 zFSName = "FAT-16 ";
1466 break;
1467 case PAR_EXTENDED:
1468 zFSName = "EXTEND ";
1469 break;
1470 case PAR_FAT16BIG:
1471 zFSName = "BIGDOS ";
1472 break;
1473 case PAR_HPFS:
1474 zFSName = "HPFS ";
1475 break;
1476 case PAR_AIXBOOT:
1477 zFSName = "AIX ";
1478 break;
1479 case PAR_AIXDATA:
1480 zFSName = "AIX ";
1481 break;
1482 case PAR_BOOTMANAGER:
1483 zFSName = "BOOTMNG";
1484 break;
1485 case PAR_WINDOWS95:
1486 zFSName = "WIN95 ";
1487 break;
1488 case PAR_WINDOWS95LB:
1489 zFSName = "WIN95 ";
1490 break;
1491 case PAR_VFAT16BIG:
1492 zFSName = "VFAT ";
1493 break;
1494 case PAR_VFAT16EXT:
1495 zFSName = "VFAT ";
1496 break;
1497 case PAR_OPUS:
1498 zFSName = "OPUS ";
1499 break;
1500 case PAR_HID12SMALL:
1501 zFSName = "FAT-12*";
1502 break;
1503 case PAR_COMPAQDIAG:
1504 zFSName = "COMPAQ ";
1505 break;
1506 case PAR_HID16SMALL:
1507 zFSName = "FAT-16*";
1508 break;
1509 case PAR_HID16BIG:
1510 zFSName = "BIGDOS*";
1511 break;
1512 case PAR_HIDHPFS:
1513 zFSName = "HPFS* ";
1514 break;
1515 case PAR_WINDOWSSWP:
1516 zFSName = "WINSWAP";
1517 break;
1518 case PAR_NECDOS:
1519 zFSName = "NECDOS ";
1520 break;
1521 case PAR_THEOS:
1522 zFSName = "THEOS ";
1523 break;
1524 case PAR_VENIX:
1525 zFSName = "VENIX ";
1526 break;
1527 case PAR_RISCBOOT:
1528 zFSName = "RISC ";
1529 break;
1530 case PAR_SFS:
1531 zFSName = "SFS ";
1532 break;
1533 case PAR_ONTRACK:
1534 zFSName = "ONTRACK";
1535 break;
1536 case PAR_ONTRACKEXT:
1537 zFSName = "ONTRACK";
1538 break;
1539 case PAR_CPM:
1540 zFSName = "CP/M ";
1541 break;
1542 case PAR_UNIXSYSV:
1543 zFSName = "UNIX ";
1544 break;
1545 case PAR_NOVELL_64:
1546 zFSName = "NOVELL ";
1547 break;
1548 case PAR_NOVELL_65:
1549 zFSName = "NOVELL ";
1550 break;
1551 case PAR_NOVELL_67:
1552 zFSName = "NOVELL ";
1553 break;
1554 case PAR_NOVELL_68:
1555 zFSName = "NOVELL ";
1556 break;
1557 case PAR_NOVELL_69:
1558 zFSName = "NOVELL ";
1559 break;
1560 case PAR_PCIX:
1561 zFSName = "PCIX ";
1562 break;
1563 case PAR_MINIX:
1564 zFSName = "MINIX ";
1565 break;
1566 case PAR_LINUX:
1567 zFSName = "LINUX ";
1568 break;
1569 case PAR_LINUXSWAP:
1570 zFSName = "LNXSWP ";
1571 break;
1572 case PAR_LINUXFILE:
1573 zFSName = "LINUX ";
1574 break;
1575 case PAR_FREEBSD:
1576 zFSName = "FREEBSD";
1577 break;
1578 case PAR_BBT:
1579 zFSName = "BBT ";
1580 break;
1581
1582 default:
1583 zFSName = " ";
1584 break;
1585 }
1586 return zFSName;
1587}
1588
1589/*
1590 * AppendPartition:
1591 * this appends the given partition information to
1592 * the given partition list. To do this, a new
1593 * PARTITIONINFO structure is created and appended
1594 * in a list (managed thru the PARTITIONINFO.pNext
1595 * items).
1596 *
1597 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
1598 * With each call of this function, this pointer is advanced
1599 * to point to the newly created PARTITIONINFO, so before
1600 * calling this function for the first time,
1601 *
1602 * Based on code (C) Dmitry A. Steklenev.
1603 *
1604 *@@added V0.9.0 [umoeller]
1605 */
1606
1607APIRET AppendPartition(PARTITIONINFO **pppiFirst,
1608 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
1609 PUSHORT posCount, // in/out: partition count
1610 BYTE bDisk, // in: disk of partition
1611 char *pszBootName, // in: boot partition name
1612 CHAR cLetter, // in/out: drive letter
1613 BYTE bFsType, // in: file system type
1614 BOOL fPrimary, // in: primary?
1615 BOOL fBootable,
1616 ULONG ulSectors) // in: no. of sectors
1617{
1618 APIRET arc = NO_ERROR;
1619 PPARTITIONINFO ppiNew = (PPARTITIONINFO)malloc(sizeof(PARTITIONINFO));
1620 if (ppiNew)
1621 {
1622 // store data
1623 ppiNew->bDisk = bDisk;
1624 if (fBootable)
1625 {
1626 memcpy(ppiNew->szBootName, pszBootName, 8);
1627 ppiNew->szBootName[8] = 0;
1628 }
1629 else
1630 ppiNew->szBootName[0] = 0;
1631 ppiNew->cLetter = cLetter;
1632 ppiNew->bFSType = bFsType;
1633 strcpy(ppiNew->szFSType,
1634 doshType2FSName(bFsType));
1635 ppiNew->fPrimary = fPrimary;
1636 ppiNew->fBootable = fBootable;
1637 ppiNew->ulSize = ulSectors / 2048;
1638
1639 ppiNew->pNext = NULL;
1640
1641 (*posCount)++;
1642
1643 if (*pppiFirst == (PPARTITIONINFO)NULL)
1644 {
1645 // first call:
1646 *pppiFirst = ppiNew;
1647 *pppiThis = ppiNew;
1648 }
1649 else
1650 {
1651 // append to list
1652 (**pppiThis).pNext = ppiNew;
1653 *pppiThis = ppiNew;
1654 }
1655 }
1656 else
1657 arc = ERROR_NOT_ENOUGH_MEMORY;
1658
1659 return (arc);
1660}
1661
1662// Sector and Cylinder values are actually 6 bits and 10 bits:
1663//
1664// 1 1 1 1 1 1
1665// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
1666// ³c c c c c c c c C c S s s s s s³
1667// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
1668//
1669// The high two bits of the second byte are used as the high bits
1670// of a 10-bit value. This allows for as many as 1024 cylinders
1671// and 64 sectors per cylinder.
1672
1673/*
1674 * GetCyl:
1675 * get cylinder number.
1676 *
1677 * Based on code (C) Dmitry A. Steklenev.
1678 *
1679 *@@added V0.9.0 [umoeller]
1680 */
1681
1682static USHORT GetCyl(USHORT rBeginSecCyl)
1683{
1684 return ((rBeginSecCyl & 0x00C0) << 2) +
1685 ((rBeginSecCyl & 0xFF00) >> 8);
1686}
1687
1688/*
1689 * GetSec:
1690 * get sector number.
1691 *
1692 * Based on code (C) Dmitry A. Steklenev.
1693 *
1694 *@@added V0.9.0 [umoeller]
1695 */
1696
1697static USHORT GetSec(USHORT rBeginSecCyl)
1698{
1699 return rBeginSecCyl & 0x003F;
1700}
1701
1702/*
1703 *@@ doshGetBootManager:
1704 * this goes thru the master boot records on all
1705 * disks to find the boot manager partitions.
1706 *
1707 * Returns:
1708 * -- NO_ERROR: boot manager found; in that case,
1709 * information about the boot manager
1710 * is written into *pusDisk, *pusPart,
1711 * *BmInfo. Any of these pointers can
1712 * be NULL if you're not interested.
1713 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
1714 *
1715 * Based on code (C) Dmitry A. Steklenev.
1716 *
1717 *@@added V0.9.0 [umoeller]
1718 */
1719
1720APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
1721 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
1722 PAR_INFO *BmInfo) // out: if != NULL, boot manager partition info
1723{
1724 APIRET arc = NO_ERROR;
1725 USHORT count = doshQueryDiskCount(); // Physical disk number
1726 MBR_INFO MBoot; // Master Boot
1727 USHORT usDisk;
1728
1729 if (count > 8) // Not above 8 disks
1730 count = 8;
1731
1732 for (usDisk = 1; usDisk <= count; usDisk++)
1733 {
1734 USHORT usPrim = 0;
1735
1736 // for each disk, read the MBR, which has the
1737 // primary partitions
1738 if ((arc = doshReadSector(usDisk, &MBoot,
1739 0, 0, 1)))
1740 return (arc);
1741
1742 // Lookup BootManager partition
1743 for (usPrim = 0; usPrim < 4; usPrim++)
1744 {
1745 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
1746 {
1747 if (BmInfo)
1748 *BmInfo = MBoot.sPrtnInfo[usPrim];
1749 if (pusPart)
1750 *pusPart = usPrim;
1751 if (pusDisk)
1752 *pusDisk = usDisk;
1753 return (NO_ERROR);
1754 }
1755 }
1756 }
1757
1758 return (ERROR_NOT_SUPPORTED);
1759}
1760
1761/*
1762 * GetPrimaryPartitions:
1763 * this returns the primary partitions.
1764 *
1765 * This gets called from doshGetPartitionsList.
1766 *
1767 * Returns 0 upon errors.
1768 *
1769 * Based on code (C) Dmitry A. Steklenev.
1770 *
1771 *@@added V0.9.0 [umoeller]
1772 */
1773
1774APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
1775 PARTITIONINFO **pppiThis,
1776 PUSHORT posCount, // in/out: partition count
1777 PCHAR pcLetter, // in/out: drive letter counter
1778 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
1779 PAR_INFO* BmInfo, // in: info returned by doshGetBootManager or NULL
1780 UINT iDisk) // in: system's physical disk count
1781{
1782 APIRET arc = NO_ERROR;
1783 MBR_INFO MBoot; // Master Boot
1784 SYS_INFO MName[32]; // Name Space from Boot Manager
1785 USHORT i;
1786
1787 memset(&MName, 0, sizeof(MName));
1788
1789 if (BmInfo)
1790 {
1791 // read boot manager name table
1792 if ((arc = doshReadSector(BmDisk, &MName, BmInfo->bBeginHead,
1793 GetCyl(BmInfo->rBeginSecCyl),
1794 GetSec(BmInfo->rBeginSecCyl) + 3)))
1795 return (arc);
1796 }
1797
1798 // read master boot record
1799 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
1800 return (arc);
1801
1802 for (i = 0;
1803 i < 4; // there can be only four primary partitions
1804 i++)
1805 {
1806 // skip unused partition, BootManager or Extended partition
1807 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
1808 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER) // skip boot manager
1809 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_EXTENDED) // skip extended
1810 )
1811 {
1812 BOOL fBootable = ( (BmInfo)
1813 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
1814 );
1815 // store this partition
1816 if ((arc = AppendPartition(pppiFirst,
1817 pppiThis,
1818 posCount,
1819 iDisk,
1820 (fBootable)
1821 ? (char*)&MName[(iDisk - 1) * 4 + i].name
1822 : "",
1823 *pcLetter,
1824 MBoot.sPrtnInfo[i].bFileSysCode,
1825 TRUE, // primary
1826 fBootable,
1827 MBoot.sPrtnInfo[i].lTotalSects)))
1828 return (arc);
1829 }
1830 }
1831 return (NO_ERROR);
1832}
1833
1834/*
1835 * GetLogicalDrives:
1836 * this returns info for the logical drives
1837 * in the extended partition. This gets called
1838 * from GetExtendedPartition.
1839 *
1840 * This gets called from GetExtendedPartition.
1841 *
1842 * Based on code (C) Dmitry A. Steklenev.
1843 *
1844 *@@added V0.9.0 [umoeller]
1845 */
1846
1847APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
1848 PARTITIONINFO **pppiThis,
1849 PUSHORT posCount,
1850 PCHAR pcLetter,
1851 PAR_INFO* PrInfo, // in: MBR entry of extended partition
1852 UINT PrDisk,
1853 PAR_INFO* BmInfo)
1854{
1855 APIRET arc = NO_ERROR;
1856 EXT_INFO MBoot; // Master Boot
1857 USHORT i;
1858
1859 if ((arc = doshReadSector(PrDisk, &MBoot, PrInfo->bBeginHead,
1860 GetCyl(PrInfo->rBeginSecCyl),
1861 GetSec(PrInfo->rBeginSecCyl))))
1862 return (arc);
1863
1864 for (i = 0; i < 4; i++)
1865 {
1866 // skip unused partition or BootManager partition
1867 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
1868 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER)
1869 )
1870 {
1871 BOOL fBootable = FALSE;
1872 BOOL fAssignLetter = FALSE;
1873
1874 // special work around extended partition
1875 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
1876 {
1877 if ((arc = GetLogicalDrives(pppiFirst,
1878 pppiThis,
1879 posCount,
1880 pcLetter,
1881 &MBoot.sPrtnInfo[i],
1882 PrDisk,
1883 BmInfo)))
1884 return (arc);
1885
1886 continue;
1887 }
1888
1889 // raise driver letter if OS/2 would recognize this drive
1890 if ( (MBoot.sPrtnInfo[i].bFileSysCode < PAR_PCIX)
1891 )
1892 fAssignLetter = TRUE;
1893
1894 if (fAssignLetter)
1895 (*pcLetter)++;
1896
1897 fBootable = ( (BmInfo)
1898 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
1899 );
1900
1901 if ((arc = AppendPartition(pppiFirst,
1902 pppiThis,
1903 posCount,
1904 PrDisk,
1905 (fBootable)
1906 ? (char*)&MBoot.sBmNames[i].name
1907 : "",
1908 (fAssignLetter)
1909 ? *pcLetter
1910 : ' ',
1911 MBoot.sPrtnInfo[i].bFileSysCode,
1912 FALSE, // primary
1913 fBootable, // bootable
1914 MBoot.sPrtnInfo[i].lTotalSects)))
1915 return (arc);
1916 }
1917
1918 /* // if BootManager installed and partition is bootable
1919 if (BmInfo)
1920 {
1921 if (MBoot.sBmNames[i].bootable & 0x01)
1922 {
1923 }
1924 }
1925
1926 // if BootManager not installed
1927 else
1928 {
1929 if (arc = AppendPartition(pppiFirst,
1930 pppiThis,
1931 posCount,
1932 PrDisk,
1933 "",
1934 *pcLetter,
1935 MBoot.sPrtnInfo[i].bFileSysCode,
1936 FALSE,
1937 MBoot.sPrtnInfo[i].lTotalSects))
1938 return (arc);
1939 } */
1940 }
1941
1942 return (NO_ERROR);
1943}
1944
1945/*
1946 * GetExtendedPartition:
1947 * this finds the extended partition on the given
1948 * drive and calls GetLogicalDrives in turn.
1949 *
1950 * This gets called from doshGetPartitionsList.
1951 *
1952 * Based on code (C) Dmitry A. Steklenev.
1953 *
1954 *@@added V0.9.0 [umoeller]
1955 */
1956
1957APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
1958 PARTITIONINFO **pppiThis,
1959 PUSHORT posCount,
1960 PCHAR pcLetter,
1961 PAR_INFO* BmInfo,
1962 UINT iDisk) // in: disk to query
1963{
1964 APIRET arc = NO_ERROR;
1965 MBR_INFO MBoot; // Master Boot
1966 USHORT i;
1967
1968 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
1969 return (arc);
1970
1971 // go thru MBR entries to find extended partition
1972 for (i = 0;
1973 i < 4;
1974 i++)
1975 {
1976 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
1977 {
1978 if ((arc = GetLogicalDrives(pppiFirst,
1979 pppiThis,
1980 posCount,
1981 pcLetter,
1982 &MBoot.sPrtnInfo[i],
1983 iDisk,
1984 BmInfo)))
1985 return (arc);
1986 }
1987 }
1988
1989 return (NO_ERROR);
1990}
1991
1992/*
1993 *@@ doshGetPartitionsList:
1994 * this returns lots of information about the
1995 * partitions on all physical disks, which is
1996 * read directly from the MBRs and partition
1997 * tables.
1998 *
1999 * If NO_ERROR is returned by this function,
2000 * *ppPartitionInfo points to a linked list of
2001 * PARTITIONINFO structures, which has
2002 * *pusPartitionCount items.
2003 *
2004 * In that case, use doshFreePartitionsList to
2005 * free the resources allocated by this function.
2006 *
2007 * The linked list starts out with all the primary
2008 * partitions, followed by the logical drives in
2009 * the extended partitions. This function attempts
2010 * to guess the correct drive letters and stores
2011 * these with the PARTITIONINFO items, but there's
2012 * no guarantee that this is correct. We correctly
2013 * ignore Linux partitions here and give all primary
2014 * partitions the C: letter, but I have no idea
2015 * what happens with NTFS partitions, since I have
2016 * none.
2017 *
2018 * If an error != NO_ERROR is returned, *pusContext
2019 * will be set to one of the following:
2020 * -- 1: boot manager not found
2021 * -- 2: primary partitions error
2022 * -- 3: secondary partitions error
2023 *
2024 * Based on code (C) Dmitry A. Steklenev.
2025 *
2026 *@@added V0.9.0 [umoeller]
2027 */
2028
2029APIRET doshGetPartitionsList(PPARTITIONINFO *ppPartitionInfo, // out: partition info array
2030 PUSHORT pusPartitionCount, // out: count of items in **ppPartitionInfo
2031 PUSHORT pusContext) // out: error context
2032{
2033 APIRET arc = NO_ERROR;
2034 PAR_INFO BmInfo; // BootManager partition
2035 USHORT usBmDisk; // BootManager disk
2036 USHORT cDisks = doshQueryDiskCount(); // physical disks count
2037 USHORT i;
2038
2039 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
2040 *ppiTemp = NULL;
2041 USHORT osCount; // bootable partition count
2042 CHAR cLetter = 'C'; // first drive letter
2043
2044 if (cDisks > 8) // Not above 8 disks
2045 cDisks = 8;
2046
2047 // get boot manager disk and info
2048 if ((arc = doshGetBootManager(&usBmDisk,
2049 NULL,
2050 &BmInfo)) != NO_ERROR)
2051 {
2052 *pusContext = 1;
2053 return (arc);
2054 }
2055 // on each disk, read primary partitions
2056 for (i = 1; i <= cDisks; i++)
2057 if ((arc = GetPrimaryPartitions(&pPartitionInfos,
2058 &ppiTemp,
2059 &osCount,
2060 &cLetter,
2061 usBmDisk,
2062 usBmDisk ? &BmInfo : 0,
2063 i)))
2064 {
2065 *pusContext = 2;
2066 return (arc);
2067 }
2068
2069 if (usBmDisk)
2070 {
2071 // boot manager found:
2072 // on each disk, read extended partition
2073 // with logical drives
2074 for (i = 1; i <= cDisks; i++)
2075 if ((arc = GetExtendedPartition(&pPartitionInfos,
2076 &ppiTemp,
2077 &osCount,
2078 &cLetter,
2079 &BmInfo,
2080 i)))
2081 {
2082 *pusContext = 3;
2083 return (arc);
2084 }
2085 }
2086
2087 *ppPartitionInfo = pPartitionInfos;
2088 *pusPartitionCount = osCount;
2089
2090 return (NO_ERROR); // 0
2091}
2092
2093/*
2094 *@@ doshFreePartitionsList:
2095 * this frees the resources allocated by
2096 * doshGetPartitionsList.
2097 *
2098 *@@added V0.9.0 [umoeller]
2099 */
2100
2101APIRET doshFreePartitionsList(PPARTITIONINFO pPartitionInfo)
2102{
2103 PPARTITIONINFO ppiThis = NULL;
2104 ppiThis = pPartitionInfo;
2105 while (ppiThis)
2106 {
2107 PPARTITIONINFO ppiNext = ppiThis->pNext;
2108 free(ppiThis);
2109 ppiThis = ppiNext;
2110 }
2111 return (NO_ERROR);
2112}
2113
2114
Note: See TracBrowser for help on using the repository browser.