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

Last change on this file since 70 was 69, checked in by umoeller, 24 years ago

New folder sorting. Updated folder refresh. Misc other changes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 149.8 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\ensure.h"
67#include "helpers\standards.h"
68#include "helpers\stringh.h"
69
70#pragma hdrstop
71
72/*
73 *@@category: Helpers\Control program helpers\Miscellaneous
74 */
75
76/* ******************************************************************
77 *
78 * Miscellaneous
79 *
80 ********************************************************************/
81
82/*
83 *@@ doshIsValidFileName:
84 * this returns NO_ERROR only if pszFile is a valid file name.
85 * This may include a full path.
86 *
87 * If a drive letter is specified, this checks for whether
88 * that drive is a FAT drive and adjust the checks accordingly,
89 * i.e. 8+3 syntax (per path component).
90 *
91 * If no drive letter is specified, this check is performed
92 * for the current drive.
93 *
94 * This also checks if pszFileNames contains characters which
95 * are invalid for the current drive.
96 *
97 * Note: this performs syntactic checks only. This does not
98 * check for whether the specified path components exist.
99 * However, it _is_ checked for whether the given drive
100 * exists.
101 *
102 * This func is especially useful to check filenames that
103 * have been entered by the user in a "Save as" dialog.
104 *
105 * If an error is found, the corresponding DOS error code
106 * is returned:
107 * -- ERROR_INVALID_DRIVE
108 * -- ERROR_FILENAME_EXCED_RANGE (on FAT: no 8+3 filename)
109 * -- ERROR_INVALID_NAME (invalid character)
110 * -- ERROR_CURRENT_DIRECTORY (if fFullyQualified: no full path specified)
111 *
112 *@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
113 */
114
115APIRET doshIsValidFileName(const char* pcszFile,
116 BOOL fFullyQualified) // in: if TRUE, pcszFile must be fully q'fied
117{
118 APIRET arc = NO_ERROR;
119 CHAR szPath[CCHMAXPATH+4] = " :";
120 CHAR szComponent[CCHMAXPATH];
121 PSZ p1, p2;
122 BOOL fIsFAT = FALSE;
123 PSZ pszInvalid;
124
125 if (fFullyQualified) // V0.9.2 (2000-03-11) [umoeller]
126 {
127 if ( (*(pcszFile + 1) != ':')
128 || (*(pcszFile + 2) != '\\')
129 )
130 arc = ERROR_CURRENT_DIRECTORY;
131 }
132
133 // check drive first
134 if (*(pcszFile + 1) == ':')
135 {
136 CHAR cDrive = toupper(*pcszFile);
137 double d;
138 // drive specified:
139 strcpy(szPath, pcszFile);
140 szPath[0] = toupper(*pcszFile);
141 arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
142 }
143 else
144 {
145 // no drive specified: take current
146 ULONG ulDriveNum = 0,
147 ulDriveMap = 0;
148 arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
149 szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
150 szPath[1] = ':';
151 strcpy(&szPath[2], pcszFile);
152 }
153
154 if (arc == NO_ERROR)
155 {
156 fIsFAT = doshIsFileOnFAT(szPath);
157
158 pszInvalid = (fIsFAT)
159 ? "<>|+=:;,\"/[] " // invalid characters in FAT
160 : "<>|:\"/"; // invalid characters in IFS's
161
162 // now separate path components
163 p1 = &szPath[2]; // advance past ':'
164
165 do {
166
167 if (*p1 == '\\')
168 p1++;
169
170 p2 = strchr(p1, '\\');
171 if (p2 == NULL)
172 p2 = p1 + strlen(p1);
173
174 if (p1 != p2)
175 {
176 LONG lDotOfs = -1,
177 lAfterDot = -1;
178 ULONG cbFile,
179 ul;
180 PSZ pSource = szComponent;
181
182 strncpy(szComponent, p1, p2-p1);
183 szComponent[p2-p1] = 0;
184 cbFile = strlen(szComponent);
185
186 // now check each path component
187 for (ul = 0; ul < cbFile; ul++)
188 {
189 if (fIsFAT)
190 {
191 // on FAT: only 8 characters allowed before dot
192 if (*pSource == '.')
193 {
194 lDotOfs = ul;
195 lAfterDot = 0;
196 if (ul > 7)
197 return (ERROR_FILENAME_EXCED_RANGE);
198 }
199 }
200 // and check for invalid characters
201 if (strchr(pszInvalid, *pSource) != NULL)
202 return (ERROR_INVALID_NAME);
203
204 pSource++;
205
206 // on FAT, allow only three chars after dot
207 if (fIsFAT)
208 if (lAfterDot != -1)
209 {
210 lAfterDot++;
211 if (lAfterDot > 3)
212 return (ERROR_FILENAME_EXCED_RANGE);
213 }
214 }
215
216 // we are still missing the case of a FAT file
217 // name without extension; if so, check whether
218 // the file stem is <= 8 chars
219 if (fIsFAT)
220 if (lDotOfs == -1) // dot not found:
221 if (cbFile > 8)
222 return (ERROR_FILENAME_EXCED_RANGE);
223 }
224
225 // go for next component
226 p1 = p2+1;
227 } while (*p2);
228 }
229
230 return (arc);
231}
232
233/*
234 *@@ doshMakeRealName:
235 * this copies pszSource to pszTarget, replacing
236 * all characters which are not supported by file
237 * systems with cReplace.
238 *
239 * pszTarget must be at least the same size as pszSource.
240 * If (fIsFAT), the file name will be made FAT-compliant (8+3).
241 *
242 * Returns TRUE if characters were replaced.
243 *
244 *@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
245 */
246
247BOOL doshMakeRealName(PSZ pszTarget, // out: new real name
248 PSZ pszSource, // in: filename to translate
249 CHAR cReplace, // in: replacement char for invalid
250 // characters (e.g. '!')
251 BOOL fIsFAT) // in: make-FAT-compatible flag
252{
253 ULONG ul,
254 cbSource = strlen(pszSource);
255 LONG lDotOfs = -1,
256 lAfterDot = -1;
257 BOOL brc = FALSE;
258 PSZ pSource = pszSource,
259 pTarget = pszTarget;
260
261 const char *pcszInvalid = (fIsFAT)
262 ? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
263 : "*<>|:\"/\\"; // invalid characters in IFS's
264
265 for (ul = 0; ul < cbSource; ul++)
266 {
267 if (fIsFAT)
268 {
269 // on FAT: truncate filename if neccessary
270 if (*pSource == '.')
271 {
272 lDotOfs = ul;
273 lAfterDot = 0;
274 if (ul > 7) {
275 // only 8 characters allowed before dot,
276 // so set target ptr to dot pos
277 pTarget = pszTarget+8;
278 }
279 }
280 }
281 // and replace invalid characters
282 if (strchr(pcszInvalid, *pSource) == NULL)
283 *pTarget = *pSource;
284 else
285 {
286 *pTarget = cReplace;
287 brc = TRUE;
288 }
289 pTarget++;
290 pSource++;
291
292 // on FAT, allow only three chars after dot
293 if (fIsFAT)
294 if (lAfterDot != -1)
295 {
296 lAfterDot++;
297 if (lAfterDot > 3)
298 break;
299 }
300 }
301 *pTarget = '\0';
302
303 if (fIsFAT)
304 {
305 // we are still missing the case of a FAT file
306 // name without extension; if so, check whether
307 // the file stem is <= 8 chars
308 if (lDotOfs == -1) // dot not found:
309 if (cbSource > 8)
310 *(pszTarget+8) = 0; // truncate
311
312 // convert to upper case
313 strupr(pszTarget);
314 }
315
316 return (brc);
317}
318
319/*
320 *@@ doshSetCurrentDir:
321 * sets the current working directory
322 * to the given path.
323 *
324 * As opposed to DosSetCurrentDir, this
325 * one will change the current drive
326 * also, if one is specified.
327 *
328 *@@changed V0.9.9 (2001-04-04) [umoeller]: this returned an error even if none occured, fixed
329 */
330
331APIRET doshSetCurrentDir(const char *pcszDir)
332{
333 APIRET arc = NO_ERROR;
334 if (!pcszDir)
335 return (ERROR_INVALID_PARAMETER);
336 {
337 if (*pcszDir != 0)
338 if (*(pcszDir+1) == ':')
339 {
340 // drive given:
341 CHAR cDrive = toupper(*(pcszDir));
342 // change drive
343 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
344 // 1 = A:, 2 = B:, ...
345 }
346
347 arc = DosSetCurrentDir((PSZ)pcszDir);
348 }
349
350 return (arc); // V0.9.9 (2001-04-04) [umoeller]
351}
352
353/*
354 *@@category: Helpers\Control program helpers\Environment management
355 * helpers for managing those ugly environment string arrays
356 * that are used with DosStartSession and WinStartApp.
357 */
358
359/* ******************************************************************
360 *
361 * Environment helpers
362 *
363 ********************************************************************/
364
365/*
366 *@@ doshParseEnvironment:
367 * this takes one of those ugly environment strings
368 * as used by DosStartSession and WinStartApp (with
369 * lots of zero-terminated strings one after another
370 * and a duplicate zero byte as a terminator) as
371 * input and splits it into an array of separate
372 * strings in pEnv.
373 *
374 * The newly allocated strings are stored in in
375 * pEnv->papszVars. The array count is stored in
376 * pEnv->cVars.
377 *
378 * Each environment variable will be copied into
379 * one newly allocated string in the array. Use
380 * doshFreeEnvironment to free the memory allocated
381 * by this function.
382 *
383 * Use the following code to browse thru the array:
384 +
385 + DOSENVIRONMENT Env = {0};
386 + if (doshParseEnvironment(pszEnv,
387 + &Env)
388 + == NO_ERROR)
389 + {
390 + if (Env.papszVars)
391 + {
392 + PSZ *ppszThis = Env.papszVars;
393 + for (ul = 0;
394 + ul < Env.cVars;
395 + ul++)
396 + {
397 + PSZ pszThis = *ppszThis;
398 + // pszThis now has something like PATH=C:\TEMP
399 + // ...
400 + // next environment string
401 + ppszThis++;
402 + }
403 + }
404 + doshFreeEnvironment(&Env);
405 + }
406 *
407 *@@added V0.9.4 (2000-08-02) [umoeller]
408 */
409
410APIRET doshParseEnvironment(const char *pcszEnv,
411 PDOSENVIRONMENT pEnv)
412{
413 APIRET arc = NO_ERROR;
414 if (!pcszEnv)
415 arc = ERROR_INVALID_PARAMETER;
416 else
417 {
418 PSZ pszVarThis = (PSZ)pcszEnv;
419 ULONG cVars = 0;
420 // count strings
421 while (*pszVarThis)
422 {
423 cVars++;
424 pszVarThis += strlen(pszVarThis) + 1;
425 }
426
427 pEnv->cVars = cVars;
428 pEnv->papszVars = 0;
429
430 if (cVars)
431 {
432 PSZ *papsz = (PSZ*)malloc(sizeof(PSZ) * cVars);
433 if (!papsz)
434 arc = ERROR_NOT_ENOUGH_MEMORY;
435 else
436 {
437 PSZ *ppszTarget = papsz;
438 memset(papsz, 0, sizeof(PSZ) * cVars);
439 pszVarThis = (PSZ)pcszEnv;
440 while (*pszVarThis)
441 {
442 *ppszTarget = strdup(pszVarThis);
443 ppszTarget++;
444 pszVarThis += strlen(pszVarThis) + 1;
445 }
446
447 pEnv->papszVars = papsz;
448 }
449 }
450 }
451
452 return (arc);
453}
454
455/*
456 *@@ doshGetEnvironment:
457 * calls doshParseEnvironment for the current
458 * process environment, which is retrieved from
459 * the info blocks.
460 *
461 *@@added V0.9.4 (2000-07-19) [umoeller]
462 */
463
464APIRET doshGetEnvironment(PDOSENVIRONMENT pEnv)
465{
466 APIRET arc = NO_ERROR;
467 if (!pEnv)
468 arc = ERROR_INVALID_PARAMETER;
469 else
470 {
471 PTIB ptib = 0;
472 PPIB ppib = 0;
473 arc = DosGetInfoBlocks(&ptib, &ppib);
474 if (arc == NO_ERROR)
475 {
476 PSZ pszEnv = ppib->pib_pchenv;
477 if (pszEnv)
478 arc = doshParseEnvironment(pszEnv, pEnv);
479 else
480 arc = ERROR_BAD_ENVIRONMENT;
481 }
482 }
483
484 return (arc);
485}
486
487/*
488 *@@ doshFindEnvironmentVar:
489 * returns the PSZ* in the pEnv->papszVars array
490 * which specifies the environment variable in pszVarName.
491 *
492 * With pszVarName, you can either specify the variable
493 * name only ("VARNAME") or a full environment string
494 * ("VARNAME=BLAH"). In any case, only the variable name
495 * is compared.
496 *
497 * Returns NULL if no such variable name was found in
498 * the array.
499 *
500 *@@added V0.9.4 (2000-07-19) [umoeller]
501 */
502
503PSZ* doshFindEnvironmentVar(PDOSENVIRONMENT pEnv,
504 PSZ pszVarName)
505{
506 PSZ *ppszRet = 0;
507 if (pEnv)
508 {
509 if ((pEnv->papszVars) && (pszVarName))
510 {
511 PSZ *ppszThis = pEnv->papszVars;
512 PSZ pszThis;
513 ULONG ul = 0;
514 ULONG ulVarNameLen = 0;
515
516 PSZ pszSearch = NULL; // receives "VARNAME="
517 PSZ pFirstEqual = strchr(pszVarName, '=');
518 if (pFirstEqual)
519 pszSearch = strhSubstr(pszVarName, pFirstEqual + 1);
520 else
521 {
522 ulVarNameLen = strlen(pszVarName);
523 pszSearch = (PSZ)malloc(ulVarNameLen + 2);
524 memcpy(pszSearch, pszVarName, ulVarNameLen);
525 *(pszSearch + ulVarNameLen) = '=';
526 *(pszSearch + ulVarNameLen + 1) = 0;
527 }
528
529 ulVarNameLen = strlen(pszSearch);
530
531 for (ul = 0;
532 ul < pEnv->cVars;
533 ul++)
534 {
535 pszThis = *ppszThis;
536
537 if (strnicmp(*ppszThis, pszSearch, ulVarNameLen) == 0)
538 {
539 ppszRet = ppszThis;
540 break;
541 }
542
543 // next environment string
544 ppszThis++;
545 }
546 }
547 }
548
549 return (ppszRet);
550}
551
552/*
553 *@@ doshSetEnvironmentVar:
554 * sets an environment variable in the specified
555 * environment, which must have been initialized
556 * using doshGetEnvironment first.
557 *
558 * pszNewEnv must be a full environment string
559 * in the form "VARNAME=VALUE".
560 *
561 * If "VARNAME" has already been set to something
562 * in the string array in pEnv, that array item
563 * is replaced.
564 *
565 * OTOH, if "VARNAME" has not been set yet, a new
566 * item is added to the array, and pEnv->cVars is
567 * raised by one. In that case, fAddFirst determines
568 * whether the new array item is added to the front
569 * or the tail of the environment list.
570 *
571 *@@added V0.9.4 (2000-07-19) [umoeller]
572 *@@changed V0.9.7 (2000-12-17) [umoeller]: added fAddFirst
573 */
574
575APIRET doshSetEnvironmentVar(PDOSENVIRONMENT pEnv,
576 PSZ pszNewEnv,
577 BOOL fAddFirst)
578{
579 APIRET arc = NO_ERROR;
580 if (!pEnv)
581 arc = ERROR_INVALID_PARAMETER;
582 else
583 {
584 if ((!pEnv->papszVars) || (!pszNewEnv))
585 arc = ERROR_INVALID_PARAMETER;
586 else
587 {
588 PSZ *ppszEnvLine = doshFindEnvironmentVar(pEnv, pszNewEnv);
589 if (ppszEnvLine)
590 {
591 // was set already: replace
592 free(*ppszEnvLine);
593 *ppszEnvLine = strdup(pszNewEnv);
594 if (!(*ppszEnvLine))
595 arc = ERROR_NOT_ENOUGH_MEMORY;
596 }
597 else
598 {
599 PSZ *ppszNew = NULL;
600 PSZ *papszNew = NULL;
601 // not set already:
602 if (fAddFirst)
603 {
604 // add as first entry:
605 papszNew = (PSZ*)malloc(sizeof(PSZ) * (pEnv->cVars + 1));
606 // overwrite first entry
607 ppszNew = papszNew;
608 // copy old entries
609 memcpy(papszNew + 1, // second new entry
610 pEnv->papszVars, // first old entry
611 sizeof(PSZ) * pEnv->cVars);
612 }
613 else
614 {
615 // append at the tail:
616 // reallocate array and add new string
617 papszNew = (PSZ*)realloc(pEnv->papszVars,
618 sizeof(PSZ) * (pEnv->cVars + 1));
619 // overwrite last entry
620 ppszNew = papszNew + pEnv->cVars;
621 }
622
623 if (!papszNew)
624 arc = ERROR_NOT_ENOUGH_MEMORY;
625 else
626 {
627 pEnv->papszVars = papszNew;
628 pEnv->cVars++;
629 *ppszNew = strdup(pszNewEnv);
630 }
631 }
632 }
633 }
634
635 return (arc);
636}
637
638/*
639 *@@ doshConvertEnvironment:
640 * converts an environment initialized by doshGetEnvironment
641 * to the string format required by WinStartApp and DosExecPgm,
642 * that is, one memory block is allocated in *ppszEnv and all
643 * strings in pEnv->papszVars are copied to that block. Each
644 * string is terminated with a null character; the last string
645 * is terminated with two null characters.
646 *
647 * Use free() to free the memory block allocated by this
648 * function in *ppszEnv.
649 *
650 *@@added V0.9.4 (2000-07-19) [umoeller]
651 */
652
653APIRET doshConvertEnvironment(PDOSENVIRONMENT pEnv,
654 PSZ *ppszEnv, // out: environment string
655 PULONG pulSize) // out: size of block allocated in *ppszEnv; ptr can be NULL
656{
657 APIRET arc = NO_ERROR;
658 if (!pEnv)
659 arc = ERROR_INVALID_PARAMETER;
660 else
661 {
662 if (!pEnv->papszVars)
663 arc = ERROR_INVALID_PARAMETER;
664 else
665 {
666 // count memory needed for all strings
667 ULONG cbNeeded = 0,
668 ul = 0;
669 PSZ *ppszThis = pEnv->papszVars;
670
671 for (ul = 0;
672 ul < pEnv->cVars;
673 ul++)
674 {
675 cbNeeded += strlen(*ppszThis) + 1; // length of string plus null terminator
676
677 // next environment string
678 ppszThis++;
679 }
680
681 cbNeeded++; // for another null terminator
682
683 *ppszEnv = (PSZ)malloc(cbNeeded);
684 if (!(*ppszEnv))
685 arc = ERROR_NOT_ENOUGH_MEMORY;
686 else
687 {
688 PSZ pTarget = *ppszEnv;
689 if (pulSize)
690 *pulSize = cbNeeded;
691 ppszThis = pEnv->papszVars;
692
693 // now copy each string
694 for (ul = 0;
695 ul < pEnv->cVars;
696 ul++)
697 {
698 PSZ pSource = *ppszThis;
699
700 while ((*pTarget++ = *pSource++))
701 ;
702
703 // *pTarget++ = 0; // append null terminator per string
704
705 // next environment string
706 ppszThis++;
707 }
708
709 *pTarget++ = 0; // append second null terminator
710 }
711 }
712 }
713
714 return (arc);
715}
716
717/*
718 *@@ doshFreeEnvironment:
719 * frees memory allocated by doshGetEnvironment.
720 *
721 *@@added V0.9.4 (2000-07-19) [umoeller]
722 */
723
724APIRET doshFreeEnvironment(PDOSENVIRONMENT pEnv)
725{
726 APIRET arc = NO_ERROR;
727 if (!pEnv)
728 arc = ERROR_INVALID_PARAMETER;
729 else
730 {
731 if (!pEnv->papszVars)
732 arc = ERROR_INVALID_PARAMETER;
733 else
734 {
735 PSZ *ppszThis = pEnv->papszVars;
736 PSZ pszThis;
737 ULONG ul = 0;
738
739 for (ul = 0;
740 ul < pEnv->cVars;
741 ul++)
742 {
743 pszThis = *ppszThis;
744 free(pszThis);
745 // *ppszThis = NULL;
746 // next environment string
747 ppszThis++;
748 }
749
750 free(pEnv->papszVars);
751 pEnv->cVars = 0;
752 }
753 }
754
755 return (arc);
756}
757
758/*
759 *@@category: Helpers\Control program helpers\Executable info
760 * these functions can retrieve BLDLEVEL information,
761 * imported modules information, exported functions information,
762 * and resources information from any executable module. See
763 * doshExecOpen.
764 */
765
766/********************************************************************
767 *
768 * Executable functions
769 *
770 ********************************************************************/
771
772/*
773 *@@ doshExecOpen:
774 * this opens the specified executable file
775 * (which can be an .EXE, .COM, .DLL, or
776 * driver file) for use with the other
777 * doshExec* functions.
778 *
779 * If no error occurs, NO_ERROR is returned
780 * and a pointer to a new EXECUTABLE structure
781 * is stored in *ppExec. Consider this pointer a
782 * handle and pass it to doshExecClose to clean
783 * up.
784 *
785 * If NO_ERROR is returned, all the fields through
786 * ulOS are set in EXECUTABLE. The psz* fields
787 * which follow afterwards require an additional
788 * call to doshExecQueryBldLevel.
789 *
790 * NOTE: If NO_ERROR is returned, the executable
791 * file has been opened by this function. It will
792 * only be closed when you call doshExecClose.
793 *
794 * If errors occur, this function returns the
795 * following error codes:
796 *
797 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
798 *
799 * -- ERROR_INVALID_EXE_SIGNATURE: unknown
800 * executable type... the given file probably
801 * isn't even an executable.
802 *
803 * -- ERROR_INVALID_PARAMETER: ppExec is NULL.
804 *
805 * plus those of DosOpen, DosSetFilePtr, and
806 * DosRead.
807 *
808 * The following executable types are supported
809 * (see EXECUTABLE for details):
810 *
811 * -- Plain DOS 3.x executable without new header.
812 *
813 * -- New Executable (NE), used by Win16 and
814 * 16-bit OS/2 and still many of today's drivers.
815 *
816 * -- Linear Executable (LX), OS/2 2.x and above.
817 *
818 * -- Portable Executable (PE), used by Win32.
819 *
820 * V0.9.12 adds support for NOSTUB executables,
821 * which are new-style executables (NE or LX)
822 * without a leading DOS header. The executable
823 * then starts directly with the NE or LX header.
824 * I am not sure whether PE supports such things
825 * as well... if so, it should be supported too.
826 *
827 *@@added V0.9.0 [umoeller]
828 *@@changed V0.9.1 (2000-02-13) [umoeller]: fixed 32-bits flag
829 *@@changed V0.9.7 (2000-12-20) [lafaix]: fixed ulNewHeaderOfs
830 *@@changed V0.9.10 (2001-04-08) [lafaix]: added PE support
831 *@@changed V0.9.10 (2001-04-08) [umoeller]: now setting ppExec only if NO_ERROR is returned
832 *@@changed V0.9.12 (2001-05-03) [umoeller]: added support for NOSTUB newstyle executables
833 */
834
835APIRET doshExecOpen(const char* pcszExecutable,
836 PEXECUTABLE* ppExec)
837{
838 APIRET arc = NO_ERROR;
839
840 ULONG ulAction = 0;
841 HFILE hFile;
842 PEXECUTABLE pExec = NULL;
843
844 if (!ppExec)
845 return (ERROR_INVALID_PARAMETER);
846
847 pExec = (PEXECUTABLE)malloc(sizeof(EXECUTABLE));
848 if (!(pExec))
849 return (ERROR_NOT_ENOUGH_MEMORY);
850
851 memset(pExec, 0, sizeof(EXECUTABLE));
852
853 if (!(arc = DosOpen((PSZ)pcszExecutable,
854 &hFile,
855 &ulAction, // out: action taken
856 0, // in: new file (ignored for read-mode)
857 0, // in: new file attribs (ignored)
858 // open-flags
859 OPEN_ACTION_FAIL_IF_NEW
860 | OPEN_ACTION_OPEN_IF_EXISTS,
861 // open-mode
862 OPEN_FLAGS_FAIL_ON_ERROR // report errors to caller
863 | OPEN_FLAGS_SEQUENTIAL
864 | OPEN_FLAGS_NOINHERIT
865 | OPEN_SHARE_DENYNONE
866 | OPEN_ACCESS_READONLY, // read-only mode
867 NULL))) // no EAs
868 {
869 // file opened successfully:
870
871 ULONG ulLocal = 0;
872
873 // read old DOS EXE header
874 pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
875 if (!(pExec->pDosExeHeader))
876 arc = ERROR_NOT_ENOUGH_MEMORY;
877 else
878 {
879 ULONG ulBytesRead = 0;
880
881 if ( (!(arc = DosSetFilePtr(hFile,
882 0L,
883 FILE_BEGIN,
884 &ulLocal))) // out: new offset
885 && (!(arc = DosRead(hFile,
886 pExec->pDosExeHeader,
887 sizeof(DOSEXEHEADER),
888 &(pExec->cbDosExeHeader))))
889 )
890 {
891 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
892 BOOL fLoadNewHeader = FALSE;
893
894 // now check if we really have a DOS header
895 if (pExec->pDosExeHeader->usDosExeID != 0x5a4d)
896 {
897 // arc = ERROR_INVALID_EXE_SIGNATURE;
898
899 // V0.9.12 (2001-05-03) [umoeller]
900 // try loading new header directly; there are
901 // drivers which were built with NOSTUB, and
902 // the exe image starts out with the NE or LX
903 // image directly
904 fLoadNewHeader = TRUE;
905 // ulNewHeaderOfs is 0 now
906
907 // remove the DOS header info, since we have none
908 // V0.9.12 (2001-05-03) [umoeller]
909 free (pExec->pDosExeHeader);
910 pExec->pDosExeHeader = 0;
911 }
912 else
913 {
914 // we have a DOS header:
915 if (pExec->pDosExeHeader->usRelocTableOfs < 0x40)
916 {
917 // neither LX nor PE nor NE:
918 pExec->ulOS = EXEOS_DOS3;
919 pExec->ulExeFormat = EXEFORMAT_OLDDOS;
920 }
921 else
922 {
923 // we have a new header offset:
924 fLoadNewHeader = TRUE;
925 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
926 }
927 }
928
929 if (fLoadNewHeader)
930 {
931 // either LX or PE or NE:
932 // read in new header...
933 // ulNewHeaderOfs is now either 0 (if no DOS header
934 // was found) or pDosExeHeader->ulNewHeaderOfs
935 // V0.9.12 (2001-05-03) [umoeller]
936 CHAR achNewHeaderType[2] = "";
937
938 if ( (!(arc = DosSetFilePtr(hFile,
939 ulNewHeaderOfs,
940 FILE_BEGIN,
941 &ulLocal)))
942 // read two chars to find out header type
943 && (!(arc = DosRead(hFile,
944 &achNewHeaderType,
945 sizeof(achNewHeaderType),
946 &ulBytesRead)))
947 )
948 {
949 PBYTE pbCheckOS = NULL;
950
951 // reset file ptr
952 DosSetFilePtr(hFile,
953 ulNewHeaderOfs,
954 FILE_BEGIN,
955 &ulLocal);
956
957 if (memcmp(achNewHeaderType, "NE", 2) == 0)
958 {
959 // New Executable:
960 pExec->ulExeFormat = EXEFORMAT_NE;
961 // allocate NE header
962 pExec->pNEHeader = (PNEHEADER)malloc(sizeof(NEHEADER));
963 if (!(pExec->pNEHeader))
964 arc = ERROR_NOT_ENOUGH_MEMORY;
965 else
966 // read in NE header
967 if (!(arc = DosRead(hFile,
968 pExec->pNEHeader,
969 sizeof(NEHEADER),
970 &(pExec->cbNEHeader))))
971 if (pExec->cbNEHeader == sizeof(NEHEADER))
972 pbCheckOS = &(pExec->pNEHeader->bTargetOS);
973 }
974 else if ( (memcmp(achNewHeaderType, "LX", 2) == 0)
975 || (memcmp(achNewHeaderType, "LE", 2) == 0)
976 // this is used by SMARTDRV.EXE
977 )
978 {
979 // OS/2 Linear Executable:
980 pExec->ulExeFormat = EXEFORMAT_LX;
981 // allocate LX header
982 pExec->pLXHeader = (PLXHEADER)malloc(sizeof(LXHEADER));
983 if (!(pExec->pLXHeader))
984 arc = ERROR_NOT_ENOUGH_MEMORY;
985 else
986 // read in LX header
987 if (!(arc = DosRead(hFile,
988 pExec->pLXHeader,
989 sizeof(LXHEADER),
990 &(pExec->cbLXHeader))))
991 if (pExec->cbLXHeader == sizeof(LXHEADER))
992 pbCheckOS = (PBYTE)(&(pExec->pLXHeader->usTargetOS));
993 }
994 else if (memcmp(achNewHeaderType, "PE", 2) == 0)
995 {
996 PEHEADER PEHeader = {0};
997
998 pExec->ulExeFormat = EXEFORMAT_PE;
999 pExec->ulOS = EXEOS_WIN32;
1000 pExec->f32Bits = TRUE;
1001
1002 // V0.9.10 (2001-04-08) [lafaix]
1003 // read in standard PE header
1004 if (!(arc = DosRead(hFile,
1005 &PEHeader,
1006 24,
1007 &ulBytesRead)))
1008 {
1009 // allocate PE header
1010 pExec->pPEHeader = (PPEHEADER)malloc(24 + PEHeader.usHeaderSize);
1011 if (!(pExec->pPEHeader))
1012 arc = ERROR_NOT_ENOUGH_MEMORY;
1013 else
1014 {
1015 // copy standard PE header
1016 memcpy(pExec->pPEHeader,
1017 &PEHeader,
1018 24);
1019
1020 // read in optional PE header
1021 if (!(arc = DosRead(hFile,
1022 &(pExec->pPEHeader->usReserved3),
1023 PEHeader.usHeaderSize,
1024 &(pExec->cbPEHeader))))
1025 pExec->cbPEHeader += 24;
1026 }
1027 }
1028 }
1029 else
1030 // strange type:
1031 arc = ERROR_INVALID_EXE_SIGNATURE;
1032
1033 if (pbCheckOS)
1034 // BYTE to check for operating system
1035 // (NE and LX):
1036 switch (*pbCheckOS)
1037 {
1038 case NEOS_OS2:
1039 pExec->ulOS = EXEOS_OS2;
1040 if (pExec->ulExeFormat == EXEFORMAT_LX)
1041 pExec->f32Bits = TRUE;
1042 break;
1043
1044 case NEOS_WIN16:
1045 pExec->ulOS = EXEOS_WIN16;
1046 break;
1047
1048 case NEOS_DOS4:
1049 pExec->ulOS = EXEOS_DOS4;
1050 break;
1051
1052 case NEOS_WIN386:
1053 pExec->ulOS = EXEOS_WIN386;
1054 pExec->f32Bits = TRUE;
1055 break;
1056 }
1057 } // end if (!(arc = DosSetFilePtr(hFile,
1058 }
1059 } // end if (!(arc = DosSetFilePtr(hFile,
1060 } // end if pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
1061
1062 // store exec's HFILE
1063 pExec->hfExe = hFile;
1064 } // end if (!(arc = DosOpen((PSZ)pcszExecutable,
1065
1066 if (arc != NO_ERROR)
1067 // error: clean up
1068 doshExecClose(pExec);
1069 else
1070 *ppExec = pExec;
1071
1072 return (arc);
1073}
1074
1075/*
1076 *@@ doshExecClose:
1077 * this closes an executable opened with doshExecOpen.
1078 * Always call this function if NO_ERROR was returned by
1079 * doshExecOpen.
1080 *
1081 *@@added V0.9.0 [umoeller]
1082 */
1083
1084APIRET doshExecClose(PEXECUTABLE pExec)
1085{
1086 APIRET arc = NO_ERROR;
1087 if (pExec)
1088 {
1089 if (pExec->pDosExeHeader)
1090 free(pExec->pDosExeHeader);
1091 if (pExec->pNEHeader)
1092 free(pExec->pNEHeader);
1093 if (pExec->pLXHeader)
1094 free(pExec->pLXHeader);
1095
1096 if (pExec->pszDescription)
1097 free(pExec->pszDescription);
1098 if (pExec->pszVendor)
1099 free(pExec->pszVendor);
1100 if (pExec->pszVersion)
1101 free(pExec->pszVersion);
1102 if (pExec->pszInfo)
1103 free(pExec->pszInfo);
1104
1105 if (pExec->hfExe)
1106 arc = DosClose(pExec->hfExe);
1107
1108 free(pExec);
1109 }
1110 else
1111 arc = ERROR_INVALID_PARAMETER;
1112
1113 return (arc);
1114}
1115
1116/*
1117 *@@ ParseBldLevel:
1118 * called from doshExecQueryBldLevel to parse
1119 * the BLDLEVEL string.
1120 *
1121 * On entry, caller has copied the string into
1122 * pExec->pszDescription. The string is
1123 * null-terminated.
1124 *
1125 * The BLDLEVEL string comes in two flavors.
1126 *
1127 * -- The standard format is:
1128 *
1129 + @#VENDOR:VERSION#@DESCRIPTION
1130 *
1131 * DESCRIPTION can have leading spaces, but
1132 * need to have them.
1133 *
1134 * -- However, there is an extended version
1135 * in that the DESCRIPTION field is split
1136 * up even more. The marker for this seems
1137 * to be that the description starts out
1138 * with "##1##".
1139 *
1140 + ##1## DATETIME BUILDMACHINE:ASD:LANG:CTRY:REVISION:UNKNOWN:FIXPAK@@DESCRIPTION
1141 *
1142 * The problem is that the DATETIME field comes
1143 * in several flavors. IBM uses things like
1144 *
1145 + "Thu Nov 30 15:30:37 2000 BWBLD228"
1146 *
1147 * while DANIS506.ADD has
1148 *
1149 + "15.12.2000 18:22:57 Nachtigall"
1150 *
1151 * Looks like the date/time string is standardized
1152 * to have 24 characters then.
1153 *
1154 *@@added V0.9.12 (2001-05-18) [umoeller]
1155 *@@changed V0.9.12 (2001-05-19) [umoeller]: added extended BLDLEVEL support
1156 */
1157
1158VOID ParseBldLevel(PEXECUTABLE pExec)
1159{
1160 const char *pStartOfAuthor = 0,
1161 *pStartOfVendor = 0;
1162
1163 // @#VENDOR:VERSION#@ DESCRIPTION
1164 // but skip the first byte, which has the string length
1165 pStartOfVendor = strstr(pExec->pszDescription,
1166 "@#");
1167 if (pStartOfVendor)
1168 {
1169 const char *pStartOfInfo = strstr(pStartOfVendor + 2,
1170 "#@");
1171 if (pStartOfInfo)
1172 {
1173 const char *pEndOfVendor = strchr(pStartOfVendor + 2,
1174 ':');
1175 if (pEndOfVendor)
1176 {
1177 pExec->pszVendor = strhSubstr(pStartOfVendor + 2,
1178 pEndOfVendor);
1179 pExec->pszVersion = strhSubstr(pEndOfVendor + 1,
1180 pStartOfInfo);
1181 // skip "@#" in DESCRIPTION string
1182 pStartOfInfo += 2;
1183
1184 // now check if we have extended DESCRIPTION V0.9.12 (2001-05-19) [umoeller]
1185 if ( (strlen(pStartOfInfo) > 6)
1186 && (!memcmp(pStartOfInfo, "##1##", 5))
1187 )
1188 {
1189 // yes: parse that beast
1190 const char *p = pStartOfInfo + 5;
1191
1192 // get build date/time
1193 if (strlen(p) > 24)
1194 {
1195 // date/time seems to be fixed 24 chars in length
1196 pExec->pszBuildDateTime = (PSZ)malloc(25);
1197 if (pExec->pszBuildDateTime)
1198 {
1199 memcpy(pExec->pszBuildDateTime,
1200 p,
1201 24);
1202 pExec->pszBuildDateTime[24] = '\0';
1203
1204 p += 24;
1205
1206 // now we're at the colon-separated
1207 // strings, first of which is the
1208 // build machine;
1209 // skip leading spaces
1210 while (*p == ' ')
1211 p++;
1212
1213 if (*p)
1214 {
1215 char **papsz[] =
1216 {
1217 &pExec->pszBuildMachine,
1218 &pExec->pszASD,
1219 &pExec->pszLanguage,
1220 &pExec->pszCountry,
1221 &pExec->pszRevision,
1222 &pExec->pszUnknown,
1223 &pExec->pszFixpak
1224 };
1225 ULONG ul;
1226
1227 for (ul = 0;
1228 ul < sizeof(papsz) / sizeof(papsz[0]);
1229 ul++)
1230 {
1231 BOOL fStop = FALSE;
1232 const char *pNextColon = strchr(p, ':'),
1233 *pDoubleAt = strstr(p, "@@");
1234 if (!pNextColon)
1235 {
1236 // last item:
1237 if (pDoubleAt)
1238 pNextColon = pDoubleAt;
1239 else
1240 pNextColon = p + strlen(p);
1241
1242 fStop = TRUE;
1243 }
1244
1245 if ( (fStop)
1246 || ( (pNextColon)
1247 && ( (!pDoubleAt)
1248 || (pNextColon < pDoubleAt)
1249 )
1250 )
1251 )
1252 {
1253 if (pNextColon > p + 1)
1254 *(papsz[ul]) = strhSubstr(p, pNextColon);
1255 }
1256 else
1257 break;
1258
1259 if (fStop)
1260 break;
1261
1262 p = pNextColon + 1;
1263 }
1264 }
1265 }
1266 }
1267
1268 pStartOfInfo = strstr(p,
1269 "@@");
1270 if (pStartOfInfo)
1271 pStartOfInfo += 2;
1272 }
1273
1274 // -- if we had no extended DESCRIPTION,
1275 // pStartOfInfo points to regular description now
1276 // -- if we parse the extended DESCRIPTION above,
1277 // pStartOfInfo points to after @@ now
1278 // -- if we had an error, pStartOfInfo is NULL
1279 if (pStartOfInfo)
1280 {
1281 // add the regular DESCRIPTION then
1282 // skip leading spaces in info string
1283 while (*pStartOfInfo == ' ')
1284 pStartOfInfo++;
1285 if (*pStartOfInfo) // V0.9.9 (2001-04-04) [umoeller]
1286 // and copy until end of string
1287 pExec->pszInfo = strdup(pStartOfInfo);
1288 }
1289 }
1290 }
1291 }
1292}
1293
1294/*
1295 *@@ doshExecQueryBldLevel:
1296 * this retrieves buildlevel information for an
1297 * LX or NE executable previously opened with
1298 * doshExecOpen.
1299 *
1300 * BuildLevel information must be contained in the
1301 * DESCRIPTION field of an executable's module
1302 * definition (.DEF) file. In order to be readable
1303 * by BLDLEVEL.EXE (which ships with OS/2), this
1304 * string must have the following format:
1305 *
1306 + Description '@#AUTHOR:VERSION#@ DESCRIPTION'
1307 *
1308 * Example:
1309 *
1310 + Description '@#Ulrich M”ller:0.9.0#@ XWorkplace Sound Support Module'
1311 *
1312 * The "Description" entry always ends up as the
1313 * very first entry in the non-resident name table
1314 * in LX and NE executables. So this is what we retrieve
1315 * here.
1316 *
1317 * If the first entry in that table exists, NO_ERROR is
1318 * returned and at least the pszDescription field in
1319 * EXECUTABLE is set to that information.
1320 *
1321 * If that string is in IBM BLDLEVEL format, the string
1322 * is automatically parsed, and the pszVendor, pszVersion,
1323 * and pszInfo fields are also set. In the above examples,
1324 * this would return the following information:
1325 + pszVendor = "Ulrich M”ller"
1326 + pszVersion = "0.9.0"
1327 + pszInfo = "XWorkplace Sound Support Module"
1328 *
1329 * If that string is not in BLDLEVEL format, only pszDescription
1330 * will be set. The other fields remain NULL.
1331 *
1332 * This returns the following errors:
1333 *
1334 * -- ERROR_INVALID_PARAMETER: pExec invalid
1335 *
1336 * -- ERROR_INVALID_EXE_SIGNATURE (191): pExec is not in LX or NE format
1337 *
1338 * -- ERROR_INVALID_DATA (13): non-resident name table not found.
1339 *
1340 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
1341 *
1342 * plus the error codes of DosSetFilePtr and DosRead.
1343 *
1344 *@@added V0.9.0 [umoeller]
1345 *@@changed V0.9.0 (99-10-22) [umoeller]: NE format now supported
1346 *@@changed V0.9.1 (99-12-06): fixed memory leak
1347 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
1348 *@@changed V0.9.12 (2001-05-18) [umoeller]: extracted ParseBldLevel
1349 */
1350
1351APIRET doshExecQueryBldLevel(PEXECUTABLE pExec)
1352{
1353 APIRET arc = NO_ERROR;
1354
1355 if (!pExec)
1356 arc = ERROR_INVALID_PARAMETER;
1357 else
1358 {
1359 ULONG ulNRNTOfs = 0;
1360
1361 if (pExec->ulExeFormat == EXEFORMAT_LX)
1362 {
1363 // OK, LX format:
1364 // check if we have a non-resident name table
1365 if (pExec->pLXHeader == NULL)
1366 arc = ERROR_INVALID_DATA;
1367 else if (pExec->pLXHeader->ulNonResdNameTblOfs == 0)
1368 arc = ERROR_INVALID_DATA;
1369 else
1370 ulNRNTOfs = pExec->pLXHeader->ulNonResdNameTblOfs;
1371 }
1372 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1373 {
1374 // OK, NE format:
1375 // check if we have a non-resident name table
1376 if (pExec->pNEHeader == NULL)
1377 arc = ERROR_INVALID_DATA;
1378 else if (pExec->pNEHeader->ulNonResdTblOfs == 0)
1379 arc = ERROR_INVALID_DATA;
1380 else
1381 ulNRNTOfs = pExec->pNEHeader->ulNonResdTblOfs;
1382 }
1383 else
1384 // neither LX nor NE: stop
1385 arc = ERROR_INVALID_EXE_SIGNATURE;
1386
1387 if ( (!arc)
1388 && (ulNRNTOfs)
1389 )
1390 {
1391 ULONG ulLocal = 0,
1392 ulBytesRead = 0;
1393
1394 // move EXE file pointer to offset of non-resident name table
1395 // (from LX header)
1396 if (!(arc = DosSetFilePtr(pExec->hfExe, // file is still open
1397 ulNRNTOfs, // ofs determined above
1398 FILE_BEGIN,
1399 &ulLocal)))
1400 {
1401 // allocate memory as necessary
1402 PSZ pszNameTable = (PSZ)malloc(2001); // should suffice, because each entry
1403 // may only be 255 bytes in length
1404 if (!pszNameTable)
1405 arc = ERROR_NOT_ENOUGH_MEMORY;
1406 else
1407 {
1408 if (!(arc = DosRead(pExec->hfExe,
1409 pszNameTable,
1410 2000,
1411 &ulBytesRead)))
1412 {
1413 if (*pszNameTable == 0)
1414 // first byte (length byte) is null:
1415 arc = ERROR_INVALID_DATA;
1416 else
1417 {
1418 // now copy the string, which is in Pascal format
1419 pExec->pszDescription = (PSZ)malloc((*pszNameTable) + 1); // addt'l null byte
1420 if (!pExec->pszDescription)
1421 arc = ERROR_NOT_ENOUGH_MEMORY;
1422 else
1423 {
1424 memcpy(pExec->pszDescription,
1425 pszNameTable + 1, // skip length byte
1426 *pszNameTable); // length byte
1427 // terminate string
1428 *(pExec->pszDescription + (*pszNameTable)) = 0;
1429
1430 ParseBldLevel(pExec);
1431 }
1432 }
1433 }
1434
1435 free(pszNameTable);
1436 } // end if PSZ pszNameTable = (PSZ)malloc(2001);
1437 }
1438 }
1439 } // end if (!pExec)
1440
1441 return (arc);
1442}
1443
1444/*
1445 *@@ doshExecQueryImportedModules:
1446 * returns an array of FSYSMODULE structure describing all
1447 * imported modules.
1448 *
1449 * *pcModules receives the # of items in the array (not the
1450 * array size!). Use doshFreeImportedModules to clean up.
1451 *
1452 * This returns a standard OS/2 error code, which might be
1453 * any of the codes returned by DosSetFilePtr and DosRead.
1454 * In addition, this may return:
1455 *
1456 * -- ERROR_NOT_ENOUGH_MEMORY
1457 *
1458 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1459 * than LX or NE, which is not understood by this function.
1460 *
1461 * Even if NO_ERROR is returned, the array pointer might still
1462 * be NULL if the module contains no such data.
1463 *
1464 *@@added V0.9.9 (2001-03-11) [lafaix]
1465 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1466 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1467 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
1468 *@@changed V0.9.10 (2001-04-13) [lafaix]: removed 127 characters limit
1469 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1470 */
1471
1472APIRET doshExecQueryImportedModules(PEXECUTABLE pExec,
1473 PFSYSMODULE *ppaModules, // out: modules array
1474 PULONG pcModules) // out: array item count
1475{
1476 if ( (pExec)
1477 && ( (pExec->ulOS == EXEOS_OS2)
1478 || (pExec->ulOS == EXEOS_WIN16)
1479 || (pExec->ulOS == EXEOS_WIN386)
1480 )
1481 )
1482 {
1483 ENSURE_BEGIN;
1484 ULONG cModules = 0;
1485 PFSYSMODULE paModules = NULL;
1486 int i;
1487
1488 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1489
1490 if (pExec->pDosExeHeader)
1491 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1492 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1493
1494 if (pExec->ulExeFormat == EXEFORMAT_LX)
1495 {
1496 // 32-bit OS/2 executable:
1497 cModules = pExec->pLXHeader->ulImportModTblCnt;
1498
1499 if (cModules)
1500 {
1501 ULONG cb = sizeof(FSYSMODULE) * cModules; // V0.9.9 (2001-04-03) [umoeller]
1502 ULONG ulDummy;
1503
1504 paModules = (PFSYSMODULE)malloc(cb);
1505 if (!paModules)
1506 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
1507
1508 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1509
1510 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1511 pExec->pLXHeader->ulImportModTblOfs
1512 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1513 FILE_BEGIN,
1514 &ulDummy));
1515
1516 for (i = 0; i < cModules; i++)
1517 {
1518 BYTE bLen = 0;
1519
1520 // reading the length of the module name
1521 ENSURE_SAFE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1522
1523 // reading the module name
1524 ENSURE_SAFE(DosRead(pExec->hfExe,
1525 paModules[i].achModuleName,
1526 bLen,
1527 &ulDummy));
1528
1529 // module names are not null terminated, so we must
1530 // do it now
1531 paModules[i].achModuleName[bLen] = 0;
1532 } // end for
1533 }
1534 } // end LX
1535 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1536 {
1537 // 16-bit executable:
1538 cModules = pExec->pNEHeader->usModuleTblEntries;
1539
1540 if (cModules)
1541 {
1542 ULONG cb = sizeof(FSYSMODULE) * cModules;
1543
1544 paModules = (PFSYSMODULE)malloc(cb);
1545 if (!paModules)
1546 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
1547
1548 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1549
1550 for (i = 0; i < cModules; i ++)
1551 {
1552 BYTE bLen;
1553 USHORT usOfs;
1554 ULONG ulDummy;
1555
1556 // the module reference table contains offsets
1557 // relative to the import table; we hence read
1558 // the offset in the module reference table, and
1559 // then we read the name in the import table
1560
1561 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1562 pExec->pNEHeader->usModRefTblOfs
1563 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
1564 + sizeof(usOfs) * i,
1565 FILE_BEGIN,
1566 &ulDummy));
1567
1568 ENSURE_SAFE(DosRead(pExec->hfExe, &usOfs, 2, &ulDummy));
1569
1570 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1571 pExec->pNEHeader->usImportTblOfs
1572 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
1573 + usOfs,
1574 FILE_BEGIN,
1575 &ulDummy));
1576
1577 ENSURE_SAFE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1578
1579 ENSURE_SAFE(DosRead(pExec->hfExe,
1580 paModules[i].achModuleName,
1581 bLen,
1582 &ulDummy));
1583
1584 paModules[i].achModuleName[bLen] = 0;
1585 } // end for
1586 }
1587 } // end NE
1588 else
1589 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1590
1591 // no error: output data
1592 *ppaModules = paModules;
1593 *pcModules = cModules;
1594
1595 ENSURE_FINALLY;
1596 // if we had an error above, clean up
1597 free(paModules);
1598 ENSURE_END;
1599 }
1600 else
1601 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1602
1603 ENSURE_OK;
1604}
1605
1606/*
1607 *@@ doshExecFreeImportedModules:
1608 * frees resources allocated by doshExecQueryImportedModules.
1609 *
1610 *@@added V0.9.9 (2001-03-11)
1611 */
1612
1613APIRET doshExecFreeImportedModules(PFSYSMODULE paModules)
1614{
1615 free(paModules);
1616 return (NO_ERROR);
1617}
1618
1619/*
1620 *@@ ScanLXEntryTable:
1621 * returns the number of exported entries in the entry table.
1622 *
1623 * If paFunctions is not NULL, then successive entries are
1624 * filled with the found type and ordinal values.
1625 *
1626 *@@added V0.9.9 (2001-03-30) [lafaix]
1627 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1628 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1629 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1630 */
1631
1632APIRET ScanLXEntryTable(PEXECUTABLE pExec,
1633 PFSYSFUNCTION paFunctions,
1634 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1635{
1636 ULONG ulDummy;
1637 USHORT usOrdinal = 1,
1638 usCurrent = 0;
1639 int i;
1640
1641 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1642
1643 if (pExec->pDosExeHeader)
1644 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1645 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1646
1647 ENSURE(DosSetFilePtr(pExec->hfExe,
1648 pExec->pLXHeader->ulEntryTblOfs
1649 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1650 FILE_BEGIN,
1651 &ulDummy));
1652
1653 while (TRUE)
1654 {
1655 BYTE bCnt,
1656 bType,
1657 bFlag;
1658
1659 ENSURE(DosRead(pExec->hfExe, &bCnt, 1, &ulDummy));
1660
1661 if (bCnt == 0)
1662 // end of the entry table
1663 break;
1664
1665 ENSURE(DosRead(pExec->hfExe, &bType, 1, &ulDummy));
1666
1667 switch (bType & 0x7F)
1668 {
1669 /*
1670 * unused entries
1671 *
1672 */
1673
1674 case 0:
1675 usOrdinal += bCnt;
1676 break;
1677
1678 /*
1679 * 16-bit entries
1680 *
1681 * the bundle type is followed by the object number
1682 * and by bCnt bFlag+usOffset entries
1683 *
1684 */
1685
1686 case 1:
1687 ENSURE(DosSetFilePtr(pExec->hfExe,
1688 sizeof(USHORT),
1689 FILE_CURRENT,
1690 &ulDummy));
1691
1692 for (i = 0; i < bCnt; i ++)
1693 {
1694 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1695
1696 if (bFlag & 0x01)
1697 {
1698 if (paFunctions)
1699 {
1700 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1701 paFunctions[usCurrent].ulType = 1;
1702 paFunctions[usCurrent].achFunctionName[0] = 0;
1703 }
1704 usCurrent++;
1705 }
1706
1707 usOrdinal++;
1708
1709 ENSURE(DosSetFilePtr(pExec->hfExe,
1710 sizeof(USHORT),
1711 FILE_CURRENT,
1712 &ulDummy));
1713
1714 } // end for
1715 break;
1716
1717 /*
1718 * 286 call gate entries
1719 *
1720 * the bundle type is followed by the object number
1721 * and by bCnt bFlag+usOffset+usCallGate entries
1722 *
1723 */
1724
1725 case 2:
1726 ENSURE(DosSetFilePtr(pExec->hfExe,
1727 sizeof(USHORT),
1728 FILE_CURRENT,
1729 &ulDummy));
1730
1731 for (i = 0; i < bCnt; i ++)
1732 {
1733 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1734
1735 if (bFlag & 0x01)
1736 {
1737 if (paFunctions)
1738 {
1739 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1740 paFunctions[usCurrent].ulType = 2;
1741 paFunctions[usCurrent].achFunctionName[0] = 0;
1742 }
1743 usCurrent++;
1744 }
1745
1746 usOrdinal++;
1747
1748 ENSURE(DosSetFilePtr(pExec->hfExe,
1749 sizeof(USHORT) + sizeof(USHORT),
1750 FILE_CURRENT,
1751 &ulDummy));
1752
1753 } // end for
1754 break;
1755
1756 /*
1757 * 32-bit entries
1758 *
1759 * the bundle type is followed by the object number
1760 * and by bCnt bFlag+ulOffset entries
1761 *
1762 */
1763
1764 case 3:
1765 ENSURE(DosSetFilePtr(pExec->hfExe,
1766 sizeof(USHORT),
1767 FILE_CURRENT,
1768 &ulDummy));
1769
1770 for (i = 0; i < bCnt; i ++)
1771 {
1772 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1773
1774 if (bFlag & 0x01)
1775 {
1776 if (paFunctions)
1777 {
1778 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1779 paFunctions[usCurrent].ulType = 3;
1780 paFunctions[usCurrent].achFunctionName[0] = 0;
1781 }
1782 usCurrent++;
1783 }
1784
1785 usOrdinal++;
1786
1787 ENSURE(DosSetFilePtr(pExec->hfExe,
1788 sizeof(ULONG),
1789 FILE_CURRENT,
1790 &ulDummy));
1791 } // end for
1792 break;
1793
1794 /*
1795 * forwarder entries
1796 *
1797 * the bundle type is followed by a reserved word
1798 * and by bCnt bFlag+usModOrd+ulOffsOrdNum entries
1799 *
1800 */
1801
1802 case 4:
1803 ENSURE(DosSetFilePtr(pExec->hfExe,
1804 sizeof(USHORT),
1805 FILE_CURRENT,
1806 &ulDummy));
1807
1808 for (i = 0; i < bCnt; i ++)
1809 {
1810 ENSURE(DosSetFilePtr(pExec->hfExe,
1811 sizeof(BYTE) + sizeof(USHORT) + sizeof(ULONG),
1812 FILE_CURRENT,
1813 &ulDummy));
1814
1815 if (paFunctions)
1816 {
1817 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1818 paFunctions[usCurrent].ulType = 4;
1819 paFunctions[usCurrent].achFunctionName[0] = 0;
1820 }
1821 usCurrent++;
1822
1823 usOrdinal++;
1824 } // end for
1825 break;
1826
1827 /*
1828 * unknown bundle type
1829 *
1830 * we don't know how to handle this bundle, so we must
1831 * stop parsing the entry table here (as we don't know the
1832 * bundle size); if paFunctions is not null, we fill it with
1833 * informative data
1834 */
1835
1836 default:
1837 if (paFunctions)
1838 {
1839 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1840 paFunctions[usCurrent].ulType = bType;
1841 sprintf(paFunctions[usCurrent].achFunctionName,
1842 "Unknown bundle type encountered (%d). Aborting entry table scan.",
1843 bType);
1844
1845 usCurrent++;
1846 }
1847 ENSURE_FAIL(ERROR_INVALID_LIST_FORMAT);
1848 // whatever
1849 // V0.9.9 (2001-04-03) [umoeller]
1850 } // end switch (bType & 0x7F)
1851 } // end while (TRUE)
1852
1853 if (pcEntries)
1854 *pcEntries = usCurrent;
1855
1856 ENSURE_OK;
1857}
1858
1859/*
1860 *@@ ScanNEEntryTable:
1861 * returns the number of exported entries in the entry table.
1862 *
1863 * if paFunctions is not NULL, then successive entries are
1864 * filled with the found type and ordinal values.
1865 *
1866 *@@added V0.9.9 (2001-03-30) [lafaix]
1867 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1868 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1869 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1870 */
1871
1872APIRET ScanNEEntryTable(PEXECUTABLE pExec,
1873 PFSYSFUNCTION paFunctions,
1874 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1875{
1876 ULONG ulDummy;
1877 USHORT usOrdinal = 1,
1878 usCurrent = 0;
1879 int i;
1880
1881 ULONG ulNewHeaderOfs = 0;
1882
1883 if (pExec->pDosExeHeader)
1884 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1885 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1886
1887 ENSURE(DosSetFilePtr(pExec->hfExe,
1888 pExec->pNEHeader->usEntryTblOfs
1889 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1890 FILE_BEGIN,
1891 &ulDummy));
1892
1893 while (TRUE)
1894 {
1895 BYTE bCnt,
1896 bType,
1897 bFlag;
1898
1899 ENSURE(DosRead(pExec->hfExe, &bCnt, 1, &ulDummy));
1900
1901 if (bCnt == 0)
1902 // end of the entry table
1903 break;
1904
1905 ENSURE(DosRead(pExec->hfExe, &bType, 1, &ulDummy));
1906
1907 if (bType)
1908 {
1909 for (i = 0; i < bCnt; i++)
1910 {
1911 ENSURE(DosRead(pExec->hfExe,
1912 &bFlag,
1913 1,
1914 &ulDummy));
1915
1916 if (bFlag & 0x01)
1917 {
1918 if (paFunctions)
1919 {
1920 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1921 paFunctions[usCurrent].ulType = 1; // 16-bit entry
1922 paFunctions[usCurrent].achFunctionName[0] = 0;
1923 }
1924 usCurrent++;
1925 }
1926
1927 usOrdinal++;
1928
1929 if (bType == 0xFF)
1930 {
1931 // moveable segment
1932 ENSURE(DosSetFilePtr(pExec->hfExe,
1933 5,
1934 FILE_CURRENT,
1935 &ulDummy));
1936 }
1937 else
1938 {
1939 // fixed segment or constant (0xFE)
1940 ENSURE(DosSetFilePtr(pExec->hfExe,
1941 2,
1942 FILE_CURRENT,
1943 &ulDummy));
1944 }
1945
1946 } // end for
1947 }
1948 else
1949 usOrdinal += bCnt;
1950 } // end while (TRUE)
1951
1952 if (pcEntries)
1953 *pcEntries = usCurrent;
1954
1955 ENSURE_OK;
1956}
1957
1958/*
1959 *@@ Compare:
1960 * binary search helper
1961 *
1962 *@@added V0.9.9 (2001-04-01) [lafaix]
1963 *@@changed V0.9.9 (2001-04-07) [umoeller]: added _Optlink, or this won't compile as C++
1964 */
1965
1966int _Optlink Compare(const void *key,
1967 const void *element)
1968{
1969 USHORT usOrdinal = *((PUSHORT) key);
1970 PFSYSFUNCTION pFunction = (PFSYSFUNCTION)element;
1971
1972 if (usOrdinal > pFunction->ulOrdinal)
1973 return (1);
1974 else if (usOrdinal < pFunction->ulOrdinal)
1975 return (-1);
1976 else
1977 return (0);
1978}
1979
1980/*
1981 *@@ ScanNameTable:
1982 * scans a resident or non-resident name table, and fills the
1983 * appropriate paFunctions entries when it encounters exported
1984 * entries names.
1985 *
1986 * This functions works for both NE and LX executables.
1987 *
1988 *@@added V0.9.9 (2001-03-30) [lafaix]
1989 *@@changed V0.9.9 (2001-04-02) [lafaix]: the first entry is special
1990 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1991 *@@changed V0.9.9 (2001-04-05) [lafaix]: removed the 127 char limit
1992 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1993 */
1994
1995APIRET ScanNameTable(PEXECUTABLE pExec,
1996 ULONG cFunctions,
1997 PFSYSFUNCTION paFunctions)
1998{
1999 ULONG ulDummy;
2000
2001 USHORT usOrdinal;
2002 PFSYSFUNCTION pFunction;
2003
2004 while (TRUE)
2005 {
2006 BYTE bLen;
2007 CHAR achName[256];
2008 int i;
2009
2010 ENSURE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
2011
2012 if (bLen == 0)
2013 // end of the name table
2014 break;
2015
2016 ENSURE(DosRead(pExec->hfExe, &achName, bLen, &ulDummy));
2017 achName[bLen] = 0;
2018
2019 ENSURE(DosRead(pExec->hfExe, &usOrdinal, sizeof(USHORT), &ulDummy));
2020
2021 if ((pFunction = (PFSYSFUNCTION)bsearch(&usOrdinal,
2022 paFunctions,
2023 cFunctions,
2024 sizeof(FSYSFUNCTION),
2025 Compare)))
2026 {
2027 memcpy(pFunction->achFunctionName,
2028 achName,
2029 bLen+1);
2030 }
2031 }
2032
2033 ENSURE_OK;
2034}
2035
2036/*
2037 *@@ doshExecQueryExportedFunctions:
2038 * returns an array of FSYSFUNCTION structure describing all
2039 * exported functions.
2040 *
2041 * *pcFunctions receives the # of items in the array (not the
2042 * array size!). Use doshFreeExportedFunctions to clean up.
2043 *
2044 * Note that the returned array only contains entry for exported
2045 * functions. Empty export entries are _not_ included.
2046 *
2047 * This returns a standard OS/2 error code, which might be
2048 * any of the codes returned by DosSetFilePtr and DosRead.
2049 * In addition, this may return:
2050 *
2051 * -- ERROR_NOT_ENOUGH_MEMORY
2052 *
2053 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
2054 * than LX or NE, which is not understood by this function.
2055 *
2056 * -- If ERROR_INVALID_LIST_FORMAT is returned, the format of an
2057 * export entry wasn't understood here.
2058 *
2059 * Even if NO_ERROR is returned, the array pointer might still
2060 * be NULL if the module contains no such data.
2061 *
2062 *@@added V0.9.9 (2001-03-11) [lafaix]
2063 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
2064 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
2065 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
2066 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
2067 */
2068
2069APIRET doshExecQueryExportedFunctions(PEXECUTABLE pExec,
2070 PFSYSFUNCTION *ppaFunctions, // out: functions array
2071 PULONG pcFunctions) // out: array item count
2072{
2073 if ( (pExec)
2074 && ( (pExec->ulOS == EXEOS_OS2)
2075 || (pExec->ulOS == EXEOS_WIN16)
2076 || (pExec->ulOS == EXEOS_WIN386)
2077 )
2078 )
2079 {
2080 ENSURE_BEGIN;
2081 ULONG cFunctions = 0;
2082 PFSYSFUNCTION paFunctions = NULL;
2083
2084 ULONG ulDummy;
2085
2086 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
2087
2088 if (pExec->pDosExeHeader)
2089 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
2090 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
2091
2092 if (pExec->ulExeFormat == EXEFORMAT_LX)
2093 {
2094 // It's a 32bit OS/2 executable
2095
2096 // the number of exported entry points is not stored
2097 // in the executable header; we have to count them in
2098 // the entry table
2099
2100 ENSURE(ScanLXEntryTable(pExec, NULL, &cFunctions));
2101
2102 // we now have the number of exported entries; let us
2103 // build them
2104
2105 if (cFunctions)
2106 {
2107 ULONG cb = sizeof(FSYSFUNCTION) * cFunctions;
2108
2109 paFunctions = (PFSYSFUNCTION)malloc(cb);
2110 if (!paFunctions)
2111 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2112
2113 // we rescan the entry table (the cost is not as bad
2114 // as it may seem, due to disk caching)
2115
2116 ENSURE_SAFE(ScanLXEntryTable(pExec, paFunctions, NULL));
2117
2118 // we now scan the resident name table entries
2119
2120 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2121 pExec->pLXHeader->ulResdNameTblOfs
2122 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2123 FILE_BEGIN,
2124 &ulDummy));
2125
2126 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
2127
2128 // we now scan the non-resident name table entries,
2129 // whose offset is _from the begining of the file_
2130
2131 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2132 pExec->pLXHeader->ulNonResdNameTblOfs,
2133 FILE_BEGIN,
2134 &ulDummy));
2135
2136 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
2137 } // end if (cFunctions)
2138 }
2139 else if (pExec->ulExeFormat == EXEFORMAT_NE)
2140 {
2141 // it's a "new" segmented 16bit executable
2142
2143 // here too the number of exported entry points
2144 // is not stored in the executable header; we
2145 // have to count them in the entry table
2146
2147 ENSURE(ScanNEEntryTable(pExec, NULL, &cFunctions));
2148
2149 // we now have the number of exported entries; let us
2150 // build them
2151
2152 if (cFunctions)
2153 {
2154 USHORT usOrdinal = 1,
2155 usCurrent = 0;
2156
2157 paFunctions = (PFSYSFUNCTION)malloc(sizeof(FSYSFUNCTION) * cFunctions);
2158 if (!paFunctions)
2159 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2160
2161 // we rescan the entry table (the cost is not as bad
2162 // as it may seem, due to disk caching)
2163
2164 ENSURE_SAFE(ScanNEEntryTable(pExec, paFunctions, NULL));
2165
2166 // we now scan the resident name table entries
2167
2168 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2169 pExec->pNEHeader->usResdNameTblOfs
2170 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2171 FILE_BEGIN,
2172 &ulDummy));
2173
2174 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
2175
2176 // we now scan the non-resident name table entries,
2177 // whose offset is _from the begining of the file_
2178
2179 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2180 pExec->pNEHeader->ulNonResdTblOfs,
2181 FILE_BEGIN,
2182 &ulDummy));
2183
2184 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
2185 }
2186 }
2187 else
2188 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2189
2190 // no error: output data
2191 *ppaFunctions = paFunctions;
2192 *pcFunctions = cFunctions;
2193
2194 ENSURE_FINALLY;
2195 // if we had an error above, clean up
2196 free(paFunctions);
2197 ENSURE_END;
2198 }
2199 else
2200 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2201
2202 ENSURE_OK;
2203}
2204
2205/*
2206 *@@ doshExecFreeExportedFunctions:
2207 * frees resources allocated by doshExecQueryExportedFunctions.
2208 *
2209 *@@added V0.9.9 (2001-03-11)
2210 */
2211
2212APIRET doshExecFreeExportedFunctions(PFSYSFUNCTION paFunctions)
2213{
2214 free(paFunctions);
2215
2216 return (NO_ERROR);
2217}
2218
2219/*
2220 *@@ doshExecQueryResources:
2221 * returns an array of FSYSRESOURCE structures describing all
2222 * available resources in the module.
2223 *
2224 * *pcResources receives the no. of items in the array
2225 * (not the array size!). Use doshExecFreeResources to clean up.
2226 *
2227 * This returns a standard OS/2 error code, which might be
2228 * any of the codes returned by DosSetFilePtr and DosRead.
2229 * In addition, this may return:
2230 *
2231 * -- ERROR_NOT_ENOUGH_MEMORY
2232 *
2233 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
2234 * than LX or NE, which is not understood by this function.
2235 *
2236 * Even if NO_ERROR is returned, the array pointer might still
2237 * be NULL if the module contains no such data.
2238 *
2239 *@@added V0.9.7 (2000-12-18) [lafaix]
2240 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
2241 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
2242 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
2243 */
2244
2245APIRET doshExecQueryResources(PEXECUTABLE pExec, // in: executable from doshExecOpen
2246 PFSYSRESOURCE *ppaResources, // out: res's array
2247 PULONG pcResources) // out: array item count
2248{
2249 if ( (pExec)
2250 && ( (pExec->ulOS == EXEOS_OS2)
2251 || (pExec->ulOS == EXEOS_WIN16)
2252 || (pExec->ulOS == EXEOS_WIN386)
2253 )
2254 )
2255 {
2256 ENSURE_BEGIN;
2257 ULONG cResources = 0;
2258 PFSYSRESOURCE paResources = NULL;
2259
2260 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
2261
2262 if (pExec->pDosExeHeader)
2263 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
2264 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
2265
2266 if (pExec->ulExeFormat == EXEFORMAT_LX)
2267 {
2268 // 32-bit OS/2 executable:
2269 cResources = pExec->pLXHeader->ulResTblCnt;
2270
2271 if (cResources)
2272 {
2273 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2274 struct rsrc32 /* Resource Table Entry */
2275 {
2276 unsigned short type; /* Resource type */
2277 unsigned short name; /* Resource name */
2278 unsigned long cb; /* Resource size */
2279 unsigned short obj; /* Object number */
2280 unsigned long offset; /* Offset within object */
2281 } rs;
2282
2283 struct o32_obj /* Flat .EXE object table entry */
2284 {
2285 unsigned long o32_size; /* Object virtual size */
2286 unsigned long o32_base; /* Object base virtual address */
2287 unsigned long o32_flags; /* Attribute flags */
2288 unsigned long o32_pagemap; /* Object page map index */
2289 unsigned long o32_mapsize; /* Number of entries in object page map */
2290 unsigned long o32_reserved; /* Reserved */
2291 } ot;
2292 #pragma pack() // V0.9.9 (2001-04-03) [umoeller]
2293
2294 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2295 ULONG ulDummy;
2296 int i;
2297
2298 paResources = (PFSYSRESOURCE)malloc(cb);
2299 if (!paResources)
2300 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2301
2302 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2303
2304 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2305 pExec->pLXHeader->ulResTblOfs
2306 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2307 FILE_BEGIN,
2308 &ulDummy));
2309
2310 for (i = 0; i < cResources; i++)
2311 {
2312 ENSURE_SAFE(DosRead(pExec->hfExe, &rs, 14, &ulDummy));
2313
2314 paResources[i].ulID = rs.name;
2315 paResources[i].ulType = rs.type;
2316 paResources[i].ulSize = rs.cb;
2317 paResources[i].ulFlag = rs.obj; // Temp storage for Object
2318 // number. Will be filled
2319 // with resource flag
2320 // later.
2321 }
2322
2323 for (i = 0; i < cResources; i++)
2324 {
2325 ULONG ulOfsThis = pExec->pLXHeader->ulObjTblOfs
2326 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
2327 + ( sizeof(ot)
2328 * (paResources[i].ulFlag - 1));
2329
2330 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2331 ulOfsThis,
2332 FILE_BEGIN,
2333 &ulDummy));
2334
2335 ENSURE_SAFE(DosRead(pExec->hfExe, &ot, sizeof(ot), &ulDummy));
2336
2337 paResources[i].ulFlag = ((ot.o32_flags & OBJWRITE)
2338 ? 0
2339 : RNPURE);
2340 paResources[i].ulFlag |= ((ot.o32_flags & OBJDISCARD)
2341 ? 4096
2342 : 0);
2343 paResources[i].ulFlag |= ((ot.o32_flags & OBJSHARED)
2344 ? RNMOVE
2345 : 0);
2346 paResources[i].ulFlag |= ((ot.o32_flags & OBJPRELOAD)
2347 ? RNPRELOAD
2348 : 0);
2349 } // end for
2350 } // end if (cResources)
2351 } // end if (pExec->ulExeFormat == EXEFORMAT_LX)
2352 else if (pExec->ulExeFormat == EXEFORMAT_NE)
2353 {
2354 if (pExec->ulOS == EXEOS_OS2)
2355 {
2356 // 16-bit OS/2 executable:
2357 cResources = pExec->pNEHeader->usResSegmCount;
2358
2359 if (cResources)
2360 {
2361 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2362 struct {unsigned short type; unsigned short name;} rti;
2363 struct new_seg /* New .EXE segment table entry */
2364 {
2365 unsigned short ns_sector; /* File sector of start of segment */
2366 unsigned short ns_cbseg; /* Number of bytes in file */
2367 unsigned short ns_flags; /* Attribute flags */
2368 unsigned short ns_minalloc; /* Minimum allocation in bytes */
2369 } ns;
2370 #pragma pack()
2371
2372 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2373 ULONG ulDummy;
2374 int i;
2375
2376 paResources = (PFSYSRESOURCE)malloc(cb);
2377 if (!paResources)
2378 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2379
2380 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2381
2382 // we first read the resources IDs and types
2383
2384 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2385 pExec->pNEHeader->usResTblOfs
2386 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2387 FILE_BEGIN,
2388 &ulDummy));
2389
2390 for (i = 0; i < cResources; i++)
2391 {
2392 ENSURE_SAFE(DosRead(pExec->hfExe, &rti, sizeof(rti), &ulDummy));
2393
2394 paResources[i].ulID = rti.name;
2395 paResources[i].ulType = rti.type;
2396 }
2397
2398 // we then read their sizes and flags
2399
2400 for (i = 0; i < cResources; i++)
2401 {
2402 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2403 ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
2404 + pExec->pNEHeader->usSegTblOfs
2405 + (sizeof(ns)
2406 * ( pExec->pNEHeader->usSegTblEntries
2407 - pExec->pNEHeader->usResSegmCount
2408 + i)),
2409 FILE_BEGIN,
2410 &ulDummy));
2411
2412 ENSURE_SAFE(DosRead(pExec->hfExe, &ns, sizeof(ns), &ulDummy));
2413
2414 paResources[i].ulSize = ns.ns_cbseg;
2415
2416 paResources[i].ulFlag = (ns.ns_flags & OBJPRELOAD) ? RNPRELOAD : 0;
2417 paResources[i].ulFlag |= (ns.ns_flags & OBJSHARED) ? RNPURE : 0;
2418 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? RNMOVE : 0;
2419 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? 4096 : 0;
2420 }
2421 } // end if (cResources)
2422 }
2423 else
2424 {
2425 // 16-bit Windows executable
2426 USHORT usAlignShift;
2427 ULONG ulDummy;
2428
2429 ENSURE(DosSetFilePtr(pExec->hfExe,
2430 pExec->pNEHeader->usResTblOfs
2431 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
2432 FILE_BEGIN,
2433 &ulDummy));
2434
2435 ENSURE(DosRead(pExec->hfExe,
2436 &usAlignShift,
2437 sizeof(usAlignShift),
2438 &ulDummy));
2439
2440 while (TRUE)
2441 {
2442 USHORT usTypeID;
2443 USHORT usCount;
2444
2445 ENSURE(DosRead(pExec->hfExe,
2446 &usTypeID,
2447 sizeof(usTypeID),
2448 &ulDummy));
2449
2450 if (usTypeID == 0)
2451 break;
2452
2453 ENSURE(DosRead(pExec->hfExe,
2454 &usCount,
2455 sizeof(usCount),
2456 &ulDummy));
2457
2458 ENSURE(DosSetFilePtr(pExec->hfExe,
2459 sizeof(ULONG),
2460 FILE_CURRENT,
2461 &ulDummy));
2462
2463 cResources += usCount;
2464
2465 // first pass, skip NAMEINFO table
2466 ENSURE(DosSetFilePtr(pExec->hfExe,
2467 usCount*6*sizeof(USHORT),
2468 FILE_CURRENT,
2469 &ulDummy));
2470 }
2471
2472 if (cResources)
2473 {
2474 USHORT usCurrent = 0;
2475 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2476
2477 paResources = (PFSYSRESOURCE)malloc(cb);
2478 if (!paResources)
2479 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2480
2481 memset(paResources, 0, cb);
2482
2483 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2484 pExec->pNEHeader->usResTblOfs
2485 + ulNewHeaderOfs,
2486 FILE_BEGIN,
2487 &ulDummy));
2488
2489 ENSURE_SAFE(DosRead(pExec->hfExe,
2490 &usAlignShift,
2491 sizeof(usAlignShift),
2492 &ulDummy));
2493
2494 while (TRUE)
2495 {
2496 USHORT usTypeID;
2497 USHORT usCount;
2498 int i;
2499
2500 ENSURE_SAFE(DosRead(pExec->hfExe,
2501 &usTypeID,
2502 sizeof(usTypeID),
2503 &ulDummy));
2504
2505 if (usTypeID == 0)
2506 break;
2507
2508 ENSURE_SAFE(DosRead(pExec->hfExe,
2509 &usCount,
2510 sizeof(usCount),
2511 &ulDummy));
2512
2513 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2514 sizeof(ULONG),
2515 FILE_CURRENT,
2516 &ulDummy));
2517
2518 // second pass, read NAMEINFO table
2519 for (i = 0; i < usCount; i++)
2520 {
2521 USHORT usLength,
2522 usFlags,
2523 usID;
2524
2525 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2526 sizeof(USHORT),
2527 FILE_CURRENT,
2528 &ulDummy));
2529
2530 ENSURE_SAFE(DosRead(pExec->hfExe,
2531 &usLength,
2532 sizeof(USHORT),
2533 &ulDummy));
2534 ENSURE_SAFE(DosRead(pExec->hfExe,
2535 &usFlags,
2536 sizeof(USHORT),
2537 &ulDummy));
2538 ENSURE_SAFE(DosRead(pExec->hfExe,
2539 &usID,
2540 sizeof(USHORT),
2541 &ulDummy));
2542
2543 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2544 2*sizeof(USHORT),
2545 FILE_CURRENT,
2546 &ulDummy));
2547
2548 // !!! strings ids and types not handled yet
2549 // !!! 15th bit is used to denotes strings
2550 // !!! offsets [lafaix]
2551 paResources[usCurrent].ulType = usTypeID ^ 0x8000;
2552 paResources[usCurrent].ulID = usID ^ 0x8000;
2553 paResources[usCurrent].ulSize = usLength << usAlignShift;
2554 paResources[usCurrent].ulFlag = usFlags & 0x70;
2555
2556 usCurrent++;
2557 }
2558 }
2559 }
2560 }
2561 } // end else if (pExec->ulExeFormat == EXEFORMAT_NE)
2562 else
2563 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2564
2565 *ppaResources = paResources;
2566 *pcResources = cResources;
2567
2568 ENSURE_FINALLY;
2569 // if we had an error above, clean up
2570 free(paResources);
2571 ENSURE_END;
2572 }
2573 else
2574 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2575
2576 ENSURE_OK;
2577}
2578
2579/*
2580 *@@ doshExecFreeResources:
2581 * frees resources allocated by doshExecQueryResources.
2582 *
2583 *@@added V0.9.7 (2000-12-18) [lafaix]
2584 */
2585
2586APIRET doshExecFreeResources(PFSYSRESOURCE paResources)
2587{
2588 free(paResources);
2589 return (NO_ERROR);
2590}
2591
2592/*
2593 * FindFile:
2594 * helper for doshFindExecutable.
2595 *
2596 *added V0.9.11 (2001-04-25) [umoeller]
2597 */
2598
2599APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
2600 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
2601 ULONG cbExecutable) // in: sizeof (*pszExecutable)
2602{
2603 APIRET arc = NO_ERROR;
2604 FILESTATUS3 fs3;
2605
2606 if ( (strchr(pcszCommand, '\\'))
2607 || (strchr(pcszCommand, ':'))
2608 )
2609 {
2610 // looks like this is qualified:
2611 arc = DosQueryPathInfo((PSZ)pcszCommand,
2612 FIL_STANDARD,
2613 &fs3,
2614 sizeof(fs3));
2615 if (!arc)
2616 if (!(fs3.attrFile & FILE_DIRECTORY))
2617 strhncpy0(pszExecutable,
2618 pcszCommand,
2619 cbExecutable);
2620 else
2621 // directory:
2622 arc = ERROR_INVALID_EXE_SIGNATURE;
2623 }
2624 else
2625 // non-qualified:
2626 arc = DosSearchPath(SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY,
2627 "PATH",
2628 (PSZ)pcszCommand,
2629 pszExecutable,
2630 cbExecutable);
2631
2632 return (arc);
2633}
2634
2635/*
2636 *@@ doshFindExecutable:
2637 * this attempts to find an executable by doing the
2638 * following:
2639 *
2640 * 1) If pcszCommand appears to be qualified (i.e. contains
2641 * a backslash), this checks for whether the file exists.
2642 *
2643 * 2) If pcszCommand contains no backslash, this calls
2644 * DosSearchPath in order to find the full path of the
2645 * executable.
2646 *
2647 * papcszExtensions determines if additional searches are to be
2648 * performed if the file doesn't exist (case 1) or DosSearchPath
2649 * returned ERROR_FILE_NOT_FOUND (case 2).
2650 * This must point to an array of strings specifying the extra
2651 * extensions to search for.
2652 *
2653 * If both papcszExtensions and cExtensions are null, no
2654 * extra searches are performed.
2655 *
2656 * If this returns NO_ERROR, pszExecutable receives
2657 * the full path of the executable found by DosSearchPath.
2658 * Otherwise ERROR_FILE_NOT_FOUND is returned.
2659 *
2660 * Example:
2661 *
2662 + const char *aExtensions[] = { "EXE",
2663 + "COM",
2664 + "CMD"
2665 + };
2666 + CHAR szExecutable[CCHMAXPATH];
2667 + APIRET arc = doshFindExecutable("lvm",
2668 + szExecutable,
2669 + sizeof(szExecutable),
2670 + aExtensions,
2671 + 3);
2672 *
2673 *@@added V0.9.9 (2001-03-07) [umoeller]
2674 *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
2675 */
2676
2677APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
2678 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
2679 ULONG cbExecutable, // in: sizeof (*pszExecutable)
2680 const char **papcszExtensions, // in: array of extensions (without dots)
2681 ULONG cExtensions) // in: array item count
2682{
2683 APIRET arc = FindFile(pcszCommand,
2684 pszExecutable,
2685 cbExecutable);
2686
2687 if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
2688 && (cExtensions) // any extra searches wanted?
2689 )
2690 {
2691 // try additional things then
2692 PSZ psz2 = (PSZ)malloc(strlen(pcszCommand) + 20);
2693 if (psz2)
2694 {
2695 ULONG ul;
2696 for (ul = 0;
2697 ul < cExtensions;
2698 ul++)
2699 {
2700 const char *pcszExtThis = papcszExtensions[ul];
2701 sprintf(psz2, "%s.%s", pcszCommand, pcszExtThis);
2702 arc = FindFile(psz2,
2703 pszExecutable,
2704 cbExecutable);
2705 if (arc != ERROR_FILE_NOT_FOUND)
2706 break;
2707 }
2708
2709 free(psz2);
2710 }
2711 else
2712 arc = ERROR_NOT_ENOUGH_MEMORY;
2713 }
2714
2715 return (arc);
2716}
2717
2718/*
2719 *@@category: Helpers\Control program helpers\Partitions info
2720 * functions for retrieving partition information directly
2721 * from the partition tables on the disk. See doshGetPartitionsList.
2722 */
2723
2724/********************************************************************
2725 *
2726 * Partition functions
2727 *
2728 ********************************************************************/
2729
2730/*
2731 *@@ doshQueryDiskCount:
2732 * returns the no. of physical disks installed
2733 * on the system.
2734 *
2735 * Based on code (C) Dmitry A. Steklenev.
2736 *
2737 *@@added V0.9.0 [umoeller]
2738 */
2739
2740UINT doshQueryDiskCount(VOID)
2741{
2742 USHORT count = 0;
2743
2744 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &count, 2, 0, 0);
2745 return (count);
2746}
2747
2748/*
2749 *@@ doshReadSector:
2750 * reads a physical disk sector.
2751 *
2752 * If NO_ERROR is returned, the sector contents
2753 * have been stored in *buff.
2754 *
2755 * Based on code (C) Dmitry A. Steklenev.
2756 *
2757 *@@added V0.9.0 [umoeller]
2758 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
2759 */
2760
2761APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
2762 void *buff,
2763 USHORT head,
2764 USHORT cylinder,
2765 USHORT sector)
2766{
2767 APIRET arc;
2768 HFILE dh = 0;
2769 char dn[256];
2770
2771 sprintf(dn, "%u:", disk);
2772 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
2773 {
2774 TRACKLAYOUT DiskIOParm;
2775 ULONG IOCtlDataLength = sizeof(DiskIOParm);
2776 ULONG IOCtlParmLength = 512;
2777
2778 DiskIOParm.bCommand = 0;
2779 DiskIOParm.usHead = head;
2780 DiskIOParm.usCylinder = cylinder;
2781 DiskIOParm.usFirstSector = 0;
2782 DiskIOParm.cSectors = 1;
2783 DiskIOParm.TrackTable[0].usSectorNumber = sector;
2784 DiskIOParm.TrackTable[0].usSectorSize = 512;
2785
2786 arc = DosDevIOCtl(dh,
2787 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
2788 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
2789 buff , IOCtlDataLength, &IOCtlDataLength);
2790
2791 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
2792 }
2793
2794 return (arc);
2795}
2796
2797/*
2798 *@@ doshType2FSName:
2799 * this returns a static, zero-terminated string
2800 * for the given FS type. This is always 7 bytes
2801 * in length.
2802 *
2803 * Values for operating system indicator:
2804 * -- 00h empty
2805 * -- 01h DOS 12-bit FAT
2806 * -- 02h XENIX root file system
2807 * -- 03h XENIX /usr file system (obsolete)
2808 * -- 04h DOS 16-bit FAT (up to 32M)
2809 * -- 05h DOS 3.3+ extended partition
2810 * -- 06h DOS 3.31+ Large File System (16-bit FAT, over 32M)
2811 * -- 07h QNX
2812 * -- 07h OS/2 HPFS
2813 * -- 07h Windows NT NTFS
2814 * -- 07h Advanced Unix
2815 * -- 08h OS/2 (v1.0-1.3 only)
2816 * -- 08h AIX bootable partition, SplitDrive
2817 * -- 08h Commodore DOS
2818 * -- 08h DELL partition spanning multiple drives
2819 * -- 09h AIX data partition
2820 * -- 09h Coherent filesystem
2821 * -- 0Ah OS/2 Boot Manager
2822 * -- 0Ah OPUS
2823 * -- 0Ah Coherent swap partition
2824 * -- 0Bh Windows95 with 32-bit FAT
2825 * -- 0Ch Windows95 with 32-bit FAT (using LBA-mode INT 13 extensions)
2826 * -- 0Eh logical-block-addressable VFAT (same as 06h but using LBA-mode INT 13)
2827 * -- 0Fh logical-block-addressable VFAT (same as 05h but using LBA-mode INT 13)
2828 * -- 10h OPUS
2829 * -- 11h OS/2 Boot Manager hidden 12-bit FAT partition
2830 * -- 12h Compaq Diagnostics partition
2831 * -- 14h (resulted from using Novell DOS 7.0 FDISK to delete Linux Native part)
2832 * -- 14h OS/2 Boot Manager hidden sub-32M 16-bit FAT partition
2833 * -- 16h OS/2 Boot Manager hidden over-32M 16-bit FAT partition
2834 * -- 17h OS/2 Boot Manager hidden HPFS partition
2835 * -- 18h AST special Windows swap file ("Zero-Volt Suspend" partition)
2836 * -- 21h officially listed as reserved
2837 * -- 23h officially listed as reserved
2838 * -- 24h NEC MS-DOS 3.x
2839 * -- 26h officially listed as reserved
2840 * -- 31h officially listed as reserved
2841 * -- 33h officially listed as reserved
2842 * -- 34h officially listed as reserved
2843 * -- 36h officially listed as reserved
2844 * -- 38h Theos
2845 * -- 3Ch PowerQuest PartitionMagic recovery partition
2846 * -- 40h VENIX 80286
2847 * -- 41h Personal RISC Boot
2848 * -- 42h SFS (Secure File System) by Peter Gutmann
2849 * -- 50h OnTrack Disk Manager, read-only partition
2850 * -- 51h OnTrack Disk Manager, read/write partition
2851 * -- 51h NOVEL
2852 * -- 52h CP/M
2853 * -- 52h Microport System V/386
2854 * -- 53h OnTrack Disk Manager, write-only partition???
2855 * -- 54h OnTrack Disk Manager (DDO)
2856 * -- 56h GoldenBow VFeature
2857 * -- 61h SpeedStor
2858 * -- 63h Unix SysV/386, 386/ix
2859 * -- 63h Mach, MtXinu BSD 4.3 on Mach
2860 * -- 63h GNU HURD
2861 * -- 64h Novell NetWare 286
2862 * -- 65h Novell NetWare (3.11)
2863 * -- 67h Novell
2864 * -- 68h Novell
2865 * -- 69h Novell
2866 * -- 70h DiskSecure Multi-Boot
2867 * -- 71h officially listed as reserved
2868 * -- 73h officially listed as reserved
2869 * -- 74h officially listed as reserved
2870 * -- 75h PC/IX
2871 * -- 76h officially listed as reserved
2872 * -- 80h Minix v1.1 - 1.4a
2873 * -- 81h Minix v1.4b+
2874 * -- 81h Linux
2875 * -- 81h Mitac Advanced Disk Manager
2876 * -- 82h Linux Swap partition
2877 * -- 82h Prime
2878 * -- 83h Linux native file system (ext2fs/xiafs)
2879 * -- 84h OS/2-renumbered type 04h partition (related to hiding DOS C: drive)
2880 * -- 86h FAT16 volume/stripe set (Windows NT)
2881 * -- 87h HPFS Fault-Tolerant mirrored partition
2882 * -- 87h NTFS volume/stripe set
2883 * -- 93h Amoeba file system
2884 * -- 94h Amoeba bad block table
2885 * -- A0h Phoenix NoteBIOS Power Management "Save-to-Disk" partition
2886 * -- A1h officially listed as reserved
2887 * -- A3h officially listed as reserved
2888 * -- A4h officially listed as reserved
2889 * -- A5h FreeBSD, BSD/386
2890 * -- A6h officially listed as reserved
2891 * -- B1h officially listed as reserved
2892 * -- B3h officially listed as reserved
2893 * -- B4h officially listed as reserved
2894 * -- B6h officially listed as reserved
2895 * -- B7h BSDI file system (secondarily swap)
2896 * -- B8h BSDI swap partition (secondarily file system)
2897 * -- C1h DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT partition
2898 * -- C4h DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT partition
2899 * -- C6h DR DOS 6.0 LOGIN.EXE-secured Huge partition
2900 * -- C6h corrupted FAT16 volume/stripe set (Windows NT)
2901 * -- C7h Syrinx Boot
2902 * -- C7h corrupted NTFS volume/stripe set
2903 * -- D8h CP/M-86
2904 * -- DBh CP/M, Concurrent CP/M, Concurrent DOS
2905 * -- DBh CTOS (Convergent Technologies OS)
2906 * -- E1h SpeedStor 12-bit FAT extended partition
2907 * -- E3h DOS read-only
2908 * -- E3h Storage Dimensions
2909 * -- E4h SpeedStor 16-bit FAT extended partition
2910 * -- E5h officially listed as reserved
2911 * -- E6h officially listed as reserved
2912 * -- F1h Storage Dimensions
2913 * -- F2h DOS 3.3+ secondary partition
2914 * -- F3h officially listed as reserved
2915 * -- F4h SpeedStor
2916 * -- F4h Storage Dimensions
2917 * -- F6h officially listed as reserved
2918 * -- FEh LANstep
2919 * -- FEh IBM PS/2 IML
2920 * -- FFh Xenix bad block table
2921 *
2922 * Note: for partition type 07h, one should inspect the partition boot record
2923 * for the actual file system type
2924 *
2925 * Based on code (C) Dmitry A. Steklenev.
2926 *
2927 *@@added V0.9.0 [umoeller]
2928 */
2929
2930const char* doshType2FSName(unsigned char bFSType) // in: FS type
2931{
2932 PSZ zFSName = NULL;
2933
2934 switch (bFSType)
2935 {
2936 case PAR_UNUSED:
2937 zFSName = "UNUSED ";
2938 break;
2939 case PAR_FAT12SMALL:
2940 zFSName = "FAT-12 ";
2941 break;
2942 case PAR_XENIXROOT:
2943 zFSName = "XENIX ";
2944 break;
2945 case PAR_XENIXUSER:
2946 zFSName = "XENIX ";
2947 break;
2948 case PAR_FAT16SMALL:
2949 zFSName = "FAT-16 ";
2950 break;
2951 case PAR_EXTENDED:
2952 zFSName = "EXTEND ";
2953 break;
2954 case PAR_FAT16BIG:
2955 zFSName = "BIGDOS ";
2956 break;
2957 case PAR_HPFS:
2958 zFSName = "HPFS ";
2959 break;
2960 case PAR_AIXBOOT:
2961 zFSName = "AIX ";
2962 break;
2963 case PAR_AIXDATA:
2964 zFSName = "AIX ";
2965 break;
2966 case PAR_BOOTMANAGER:
2967 zFSName = "BOOTMNG";
2968 break;
2969 case PAR_WINDOWS95:
2970 zFSName = "WIN95 ";
2971 break;
2972 case PAR_WINDOWS95LB:
2973 zFSName = "WIN95 ";
2974 break;
2975 case PAR_VFAT16BIG:
2976 zFSName = "VFAT ";
2977 break;
2978 case PAR_VFAT16EXT:
2979 zFSName = "VFAT ";
2980 break;
2981 case PAR_OPUS:
2982 zFSName = "OPUS ";
2983 break;
2984 case PAR_HID12SMALL:
2985 zFSName = "FAT-12*";
2986 break;
2987 case PAR_COMPAQDIAG:
2988 zFSName = "COMPAQ ";
2989 break;
2990 case PAR_HID16SMALL:
2991 zFSName = "FAT-16*";
2992 break;
2993 case PAR_HID16BIG:
2994 zFSName = "BIGDOS*";
2995 break;
2996 case PAR_HIDHPFS:
2997 zFSName = "HPFS* ";
2998 break;
2999 case PAR_WINDOWSSWP:
3000 zFSName = "WINSWAP";
3001 break;
3002 case PAR_NECDOS:
3003 zFSName = "NECDOS ";
3004 break;
3005 case PAR_THEOS:
3006 zFSName = "THEOS ";
3007 break;
3008 case PAR_VENIX:
3009 zFSName = "VENIX ";
3010 break;
3011 case PAR_RISCBOOT:
3012 zFSName = "RISC ";
3013 break;
3014 case PAR_SFS:
3015 zFSName = "SFS ";
3016 break;
3017 case PAR_ONTRACK:
3018 zFSName = "ONTRACK";
3019 break;
3020 case PAR_ONTRACKEXT:
3021 zFSName = "ONTRACK";
3022 break;
3023 case PAR_CPM:
3024 zFSName = "CP/M ";
3025 break;
3026 case PAR_UNIXSYSV:
3027 zFSName = "UNIX ";
3028 break;
3029 case PAR_NOVELL_64:
3030 zFSName = "NOVELL ";
3031 break;
3032 case PAR_NOVELL_65:
3033 zFSName = "NOVELL ";
3034 break;
3035 case PAR_NOVELL_67:
3036 zFSName = "NOVELL ";
3037 break;
3038 case PAR_NOVELL_68:
3039 zFSName = "NOVELL ";
3040 break;
3041 case PAR_NOVELL_69:
3042 zFSName = "NOVELL ";
3043 break;
3044 case PAR_PCIX:
3045 zFSName = "PCIX ";
3046 break;
3047 case PAR_MINIX:
3048 zFSName = "MINIX ";
3049 break;
3050 case PAR_LINUX:
3051 zFSName = "LINUX ";
3052 break;
3053 case PAR_LINUXSWAP:
3054 zFSName = "LNXSWP ";
3055 break;
3056 case PAR_LINUXFILE:
3057 zFSName = "LINUX ";
3058 break;
3059 case PAR_FREEBSD:
3060 zFSName = "FREEBSD";
3061 break;
3062 case PAR_BBT:
3063 zFSName = "BBT ";
3064 break;
3065
3066 default:
3067 zFSName = " ";
3068 break;
3069 }
3070 return zFSName;
3071}
3072
3073/*
3074 * AppendPartition:
3075 * this appends the given partition information to
3076 * the given partition list. To do this, a new
3077 * PARTITIONINFO structure is created and appended
3078 * in a list (managed thru the PARTITIONINFO.pNext
3079 * items).
3080 *
3081 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
3082 * With each call of this function, this pointer is advanced
3083 * to point to the newly created PARTITIONINFO, so before
3084 * calling this function for the first time,
3085 *
3086 * Based on code (C) Dmitry A. Steklenev.
3087 *
3088 *@@added V0.9.0 [umoeller]
3089 */
3090
3091APIRET AppendPartition(PARTITIONINFO **pppiFirst,
3092 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
3093 PUSHORT posCount, // in/out: partition count
3094 BYTE bDisk, // in: disk of partition
3095 const char *pszBootName, // in: boot partition name
3096 CHAR cLetter, // in/out: drive letter
3097 BYTE bFsType, // in: file system type
3098 BOOL fPrimary, // in: primary?
3099 BOOL fBootable,
3100 ULONG ulSectors) // in: no. of sectors
3101{
3102 APIRET arc = NO_ERROR;
3103 PPARTITIONINFO ppiNew = NEW(PARTITIONINFO);
3104 if (ppiNew)
3105 {
3106 ZERO(ppiNew);
3107
3108 // store data
3109 ppiNew->bDisk = bDisk;
3110 if ((fBootable) && (pszBootName) )
3111 {
3112 memcpy(ppiNew->szBootName, pszBootName, 8);
3113 ppiNew->szBootName[8] = 0;
3114 }
3115 else
3116 ppiNew->szBootName[0] = 0;
3117 ppiNew->cLetter = cLetter;
3118 ppiNew->bFSType = bFsType;
3119 strcpy(ppiNew->szFSType,
3120 doshType2FSName(bFsType));
3121 ppiNew->fPrimary = fPrimary;
3122 ppiNew->fBootable = fBootable;
3123 ppiNew->ulSize = ulSectors / 2048;
3124
3125 ppiNew->pNext = NULL;
3126
3127 (*posCount)++;
3128
3129 if (*pppiFirst == (PPARTITIONINFO)NULL)
3130 {
3131 // first call:
3132 *pppiFirst = ppiNew;
3133 *pppiThis = ppiNew;
3134 }
3135 else
3136 {
3137 // append to list
3138 (**pppiThis).pNext = ppiNew;
3139 *pppiThis = ppiNew;
3140 }
3141 }
3142 else
3143 arc = ERROR_NOT_ENOUGH_MEMORY;
3144
3145 return (arc);
3146}
3147
3148// Sector and Cylinder values are actually 6 bits and 10 bits:
3149//
3150// 1 1 1 1 1 1
3151// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
3152// ³c c c c c c c c C c S s s s s s³
3153// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
3154//
3155// The high two bits of the second byte are used as the high bits
3156// of a 10-bit value. This allows for as many as 1024 cylinders
3157// and 64 sectors per cylinder.
3158
3159/*
3160 * GetCyl:
3161 * get cylinder number.
3162 *
3163 * Based on code (C) Dmitry A. Steklenev.
3164 *
3165 *@@added V0.9.0 [umoeller]
3166 */
3167
3168static USHORT GetCyl(USHORT rBeginSecCyl)
3169{
3170 return ( (rBeginSecCyl & 0x00C0) << 2)
3171 + ((rBeginSecCyl & 0xFF00) >> 8);
3172}
3173
3174/*
3175 * GetSec:
3176 * get sector number.
3177 *
3178 * Based on code (C) Dmitry A. Steklenev.
3179 *
3180 *@@added V0.9.0 [umoeller]
3181 */
3182
3183static USHORT GetSec(USHORT rBeginSecCyl)
3184{
3185 return rBeginSecCyl & 0x003F;
3186}
3187
3188/*
3189 *@@ doshGetBootManager:
3190 * this goes thru the master boot records on all
3191 * disks to find the boot manager partition.
3192 *
3193 * Returns:
3194 *
3195 * -- NO_ERROR: boot manager found; in that case,
3196 * information about the boot manager
3197 * is written into *pusDisk, *pusPart,
3198 * *BmInfo. Any of these pointers can
3199 * be NULL if you're not interested.
3200 *
3201 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
3202 *
3203 * Based on code (C) Dmitry A. Steklenev.
3204 *
3205 *@@added V0.9.0 [umoeller]
3206 */
3207
3208APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
3209 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
3210 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
3211{
3212 APIRET arc = NO_ERROR;
3213 USHORT count = doshQueryDiskCount(); // Physical disk number
3214 MBR_INFO MBoot; // Master Boot
3215 USHORT usDisk;
3216
3217 if (count > 8) // Not above 8 disks
3218 count = 8;
3219
3220 for (usDisk = 1; usDisk <= count; usDisk++)
3221 {
3222 USHORT usPrim = 0;
3223
3224 // for each disk, read the MBR, which has the
3225 // primary partitions
3226 if ((arc = doshReadSector(usDisk,
3227 &MBoot,
3228 0, // head
3229 0, // cylinder
3230 1))) // sector
3231 return (arc);
3232
3233 // scan primary partitions for whether
3234 // BootManager partition exists
3235 for (usPrim = 0; usPrim < 4; usPrim++)
3236 {
3237 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
3238 {
3239 // this is boot manager:
3240 if (pBmInfo)
3241 *pBmInfo = MBoot.sPrtnInfo[usPrim];
3242 if (pusPart)
3243 *pusPart = usPrim;
3244 if (pusDisk)
3245 *pusDisk = usDisk;
3246 // stop scanning
3247 return (NO_ERROR);
3248 }
3249 }
3250 }
3251
3252 return (ERROR_NOT_SUPPORTED);
3253}
3254
3255/*
3256 * GetPrimaryPartitions:
3257 * this returns the primary partitions.
3258 *
3259 * This gets called from doshGetPartitionsList.
3260 *
3261 * Returns:
3262 *
3263 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
3264 *
3265 * Based on code (C) Dmitry A. Steklenev.
3266 *
3267 *@@added V0.9.0 [umoeller]
3268 */
3269
3270APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
3271 PARTITIONINFO **pppiThis,
3272 PUSHORT posCount, // in/out: partition count
3273 PCHAR pcLetter, // in/out: drive letter counter
3274 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
3275 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
3276 UINT iDisk) // in: system's physical disk count
3277{
3278 APIRET arc = NO_ERROR;
3279
3280 if (!pBmInfo)
3281 arc = ERROR_INVALID_PARAMETER;
3282 else
3283 {
3284 SYS_INFO MName[32]; // Name Space from Boot Manager
3285 memset(&MName, 0, sizeof(MName));
3286
3287 // read boot manager name table;
3288 // this is in the boot manager primary partition
3289 // at sector offset 3 (?!?)
3290 if (!(arc = doshReadSector(BmDisk,
3291 &MName,
3292 // head, cylinder, sector of bmgr primary partition:
3293 pBmInfo->bBeginHead,
3294 GetCyl(pBmInfo->rBeginSecCyl),
3295 GetSec(pBmInfo->rBeginSecCyl) + 3)))
3296 {
3297 // got bmgr name table:
3298 MBR_INFO MBoot; // Master Boot
3299 USHORT i;
3300
3301 // read master boot record of this disk
3302 if (!(arc = doshReadSector(iDisk,
3303 &MBoot,
3304 0, // head
3305 0, // cylinder
3306 1))) // sector
3307 {
3308 for (i = 0;
3309 i < 4; // there can be only four primary partitions
3310 i++)
3311 {
3312 // skip unused partition, BootManager or Extended partition
3313 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
3314 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER) // skip boot manager
3315 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_EXTENDED) // skip extended
3316 )
3317 {
3318 BOOL fBootable = ( (pBmInfo)
3319 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
3320 );
3321 // store this partition
3322 if ((arc = AppendPartition(pppiFirst,
3323 pppiThis,
3324 posCount,
3325 iDisk,
3326 (fBootable)
3327 ? (char*)&MName[(iDisk - 1) * 4 + i].name
3328 : "",
3329 *pcLetter,
3330 MBoot.sPrtnInfo[i].bFileSysCode,
3331 TRUE, // primary
3332 fBootable,
3333 MBoot.sPrtnInfo[i].lTotalSects)))
3334 return (arc);
3335 }
3336 }
3337 }
3338 }
3339 }
3340
3341 return (arc);
3342}
3343
3344/*
3345 * GetLogicalDrives:
3346 * this returns info for the logical drives
3347 * in the extended partition. This gets called
3348 * from GetExtendedPartition.
3349 *
3350 * This gets called from GetExtendedPartition.
3351 *
3352 * Based on code (C) Dmitry A. Steklenev.
3353 *
3354 *@@added V0.9.0 [umoeller]
3355 */
3356
3357APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
3358 PARTITIONINFO **pppiThis,
3359 PUSHORT posCount,
3360 PCHAR pcLetter,
3361 PAR_INFO* PrInfo, // in: MBR entry of extended partition
3362 UINT PrDisk,
3363 PAR_INFO* BmInfo)
3364{
3365 APIRET arc = NO_ERROR;
3366 EXT_INFO MBoot; // Master Boot
3367 USHORT i;
3368
3369 if ((arc = doshReadSector(PrDisk,
3370 &MBoot,
3371 PrInfo->bBeginHead,
3372 GetCyl(PrInfo->rBeginSecCyl),
3373 GetSec(PrInfo->rBeginSecCyl))))
3374 return (arc);
3375
3376 for (i = 0; i < 4; i++)
3377 {
3378 // skip unused partition or BootManager partition
3379 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
3380 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER)
3381 )
3382 {
3383 BOOL fBootable = FALSE;
3384 BOOL fAssignLetter = FALSE;
3385
3386 // special work around extended partition
3387 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
3388 {
3389 if ((arc = GetLogicalDrives(pppiFirst,
3390 pppiThis,
3391 posCount,
3392 pcLetter,
3393 &MBoot.sPrtnInfo[i],
3394 PrDisk,
3395 BmInfo)))
3396 return (arc);
3397
3398 continue;
3399 }
3400
3401 // raise driver letter if OS/2 would recognize this drive
3402 if ( (MBoot.sPrtnInfo[i].bFileSysCode < PAR_PCIX)
3403 )
3404 fAssignLetter = TRUE;
3405
3406 if (fAssignLetter)
3407 (*pcLetter)++;
3408
3409 fBootable = ( (BmInfo)
3410 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
3411 );
3412
3413 if ((arc = AppendPartition(pppiFirst,
3414 pppiThis,
3415 posCount,
3416 PrDisk,
3417 (fBootable)
3418 ? (char*)&MBoot.sBmNames[i].name
3419 : "",
3420 (fAssignLetter)
3421 ? *pcLetter
3422 : ' ',
3423 MBoot.sPrtnInfo[i].bFileSysCode,
3424 FALSE, // primary
3425 fBootable, // bootable
3426 MBoot.sPrtnInfo[i].lTotalSects)))
3427 return (arc);
3428 }
3429 }
3430
3431 return (NO_ERROR);
3432}
3433
3434/*
3435 * GetExtendedPartition:
3436 * this finds the extended partition on the given
3437 * drive and calls GetLogicalDrives in turn.
3438 *
3439 * This gets called from doshGetPartitionsList.
3440 *
3441 * Based on code (C) Dmitry A. Steklenev.
3442 *
3443 *@@added V0.9.0 [umoeller]
3444 */
3445
3446APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
3447 PARTITIONINFO **pppiThis,
3448 PUSHORT posCount,
3449 PCHAR pcLetter,
3450 PAR_INFO* BmInfo,
3451 UINT iDisk) // in: disk to query
3452{
3453 APIRET arc = NO_ERROR;
3454 MBR_INFO MBoot; // Master Boot
3455 USHORT i;
3456
3457 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
3458 return (arc);
3459
3460 // go thru MBR entries to find extended partition
3461 for (i = 0;
3462 i < 4;
3463 i++)
3464 {
3465 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
3466 {
3467 if ((arc = GetLogicalDrives(pppiFirst,
3468 pppiThis,
3469 posCount,
3470 pcLetter,
3471 &MBoot.sPrtnInfo[i],
3472 iDisk,
3473 BmInfo)))
3474 return (arc);
3475 }
3476 }
3477
3478 return (NO_ERROR);
3479}
3480
3481/*
3482 *@@ CleanPartitionInfos:
3483 *
3484 *@@added V0.9.9 (2001-04-07) [umoeller]
3485 */
3486
3487VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
3488{
3489 while (ppiThis)
3490 {
3491 PPARTITIONINFO ppiNext = ppiThis->pNext;
3492 free(ppiThis);
3493 ppiThis = ppiNext;
3494 }
3495}
3496
3497/*
3498 *@@ doshGetPartitionsList:
3499 * this returns lots of information about the
3500 * partitions on all physical disks, which is
3501 * read directly from the MBRs and partition
3502 * tables.
3503 *
3504 * If NO_ERROR is returned by this function,
3505 * *ppPartitionInfo points to a linked list of
3506 * PARTITIONINFO structures, which has
3507 * *pusPartitionCount items.
3508 *
3509 * In that case, use doshFreePartitionsList to
3510 * free the resources allocated by this function.
3511 *
3512 * What this function returns depends on whether
3513 * LVM is installed.
3514 *
3515 * -- If LVM.DLL is found on the LIBPATH, this opens
3516 * the LVM engine and returns the info from the
3517 * LVM engine in the PARTITIONINFO structures.
3518 * The partitions are then sorted by disk in
3519 * ascending order.
3520 *
3521 * -- Otherwise, we parse the partition tables
3522 * manually. The linked list then starts out with
3523 * all the primary partitions, followed by the
3524 * logical drives in the extended partitions.
3525 * This function attempts to guess the correct drive
3526 * letters and stores these with the PARTITIONINFO
3527 * items, but there's no guarantee that this is
3528 * correct. We correctly ignore Linux partitions here
3529 * and give all primary partitions the C: letter, but
3530 * I have no idea what happens with NTFS partitions,
3531 * since I have none.
3532 *
3533 * If an error != NO_ERROR is returned, *pusContext
3534 * will be set to one of the following:
3535 *
3536 * -- 1: boot manager not found
3537 *
3538 * -- 2: primary partitions error
3539 *
3540 * -- 3: secondary partitions error
3541 *
3542 * -- 0: something else.
3543 *
3544 * Based on code (C) Dmitry A. Steklenev.
3545 *
3546 *@@added V0.9.0 [umoeller]
3547 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
3548 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
3549 */
3550
3551APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
3552 PUSHORT pusContext) // out: error context
3553{
3554 APIRET arc = NO_ERROR;
3555
3556 PLVMINFO pLVMInfo = NULL;
3557
3558 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
3559 *ppiTemp = NULL;
3560 USHORT cPartitions = 0; // bootable partition count
3561
3562 if (!ppList)
3563 return (ERROR_INVALID_PARAMETER);
3564
3565 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
3566 {
3567 // LVM installed:
3568 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
3569 &pPartitionInfos, // out: partitions array
3570 &cPartitions); // out: partitions count
3571 // copied to output below
3572
3573 if (arc)
3574 {
3575 // error: start over
3576 doshFreeLVMInfo(pLVMInfo);
3577 CleanPartitionInfos(pPartitionInfos);
3578 pPartitionInfos = NULL;
3579 cPartitions = 0;
3580 }
3581 }
3582
3583 if (arc)
3584 {
3585 // LVM not installed, or failed:
3586 // parse partitions manually
3587 PAR_INFO BmInfo; // BootManager partition
3588 USHORT usBmDisk; // BootManager disk
3589 USHORT cDisks = doshQueryDiskCount(); // physical disks count
3590 USHORT i;
3591
3592 CHAR cLetter = 'C'; // first drive letter
3593
3594 // start over
3595 arc = NO_ERROR;
3596
3597 if (cDisks > 8) // Not above 8 disks
3598 cDisks = 8;
3599
3600 // get boot manager disk and info
3601 if ((arc = doshGetBootManager(&usBmDisk,
3602 NULL,
3603 &BmInfo)) != NO_ERROR)
3604 {
3605 *pusContext = 1;
3606 }
3607 else
3608 {
3609 // on each disk, read primary partitions
3610 for (i = 1; i <= cDisks; i++)
3611 {
3612 if ((arc = GetPrimaryPartitions(&pPartitionInfos,
3613 &ppiTemp,
3614 &cPartitions,
3615 &cLetter,
3616 usBmDisk,
3617 usBmDisk ? &BmInfo : 0,
3618 i)))
3619 {
3620 *pusContext = 2;
3621 }
3622 }
3623
3624 if (!arc && usBmDisk)
3625 {
3626 // boot manager found:
3627 // on each disk, read extended partition
3628 // with logical drives
3629 for (i = 1; i <= cDisks; i++)
3630 {
3631 if ((arc = GetExtendedPartition(&pPartitionInfos,
3632 &ppiTemp,
3633 &cPartitions,
3634 &cLetter,
3635 &BmInfo,
3636 i)))
3637 {
3638 *pusContext = 3;
3639 }
3640 }
3641 }
3642 } // end else if ((arc = doshGetBootManager(&usBmDisk,
3643 } // end else if (!doshQueryLVMInfo(&pLVMInfo))
3644
3645 if (!arc)
3646 {
3647 // no error so far:
3648 *pusContext = 0;
3649
3650 *ppList = NEW(PARTITIONSLIST);
3651 if (!(*ppList))
3652 arc = ERROR_NOT_ENOUGH_MEMORY;
3653 else
3654 {
3655 ZERO(*ppList);
3656
3657 (*ppList)->pPartitionInfo = pPartitionInfos;
3658 (*ppList)->cPartitions = cPartitions;
3659
3660 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
3661 }
3662 }
3663
3664 if (arc)
3665 CleanPartitionInfos(pPartitionInfos);
3666
3667 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
3668
3669 return (arc);
3670}
3671
3672/*
3673 *@@ doshFreePartitionsList:
3674 * this frees the resources allocated by
3675 * doshGetPartitionsList.
3676 *
3677 *@@added V0.9.0 [umoeller]
3678 */
3679
3680APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
3681{
3682 if (!ppList)
3683 return (ERROR_INVALID_PARAMETER);
3684 else
3685 {
3686 CleanPartitionInfos(ppList->pPartitionInfo);
3687 doshFreeLVMInfo(ppList->pLVMInfo);
3688 free(ppList);
3689 }
3690
3691 return (NO_ERROR);
3692}
3693
3694/********************************************************************
3695 *
3696 * LVM declarations
3697 *
3698 ********************************************************************/
3699
3700/*
3701 *@@category: Helpers\Control program helpers\Partitions info\Quick LVM Interface
3702 * functions for transparently interfacing LVM.DLL.
3703 */
3704
3705typedef unsigned char BOOLEAN;
3706typedef unsigned short int CARDINAL16;
3707typedef unsigned long CARDINAL32;
3708typedef unsigned int CARDINAL;
3709typedef unsigned long DoubleWord;
3710
3711#ifdef ADDRESS
3712#undef ADDRESS
3713#endif
3714
3715typedef void* ADDRESS;
3716
3717#pragma pack(1)
3718
3719#define DISK_NAME_SIZE 20
3720#define FILESYSTEM_NAME_SIZE 20
3721#define PARTITION_NAME_SIZE 20
3722#define VOLUME_NAME_SIZE 20
3723
3724/*
3725 *@@ Drive_Control_Record:
3726 * invariant for a disk drive.
3727 *
3728 *@@added V0.9.9 (2001-04-07) [umoeller]
3729 */
3730
3731typedef struct _Drive_Control_Record
3732{
3733 CARDINAL32 Drive_Number; // OS/2 Drive Number for this drive.
3734 CARDINAL32 Drive_Size; // The total number of sectors on the drive.
3735 DoubleWord Drive_Serial_Number; // The serial number assigned to this drive. For info. purposes only.
3736 ADDRESS Drive_Handle; // Handle used for operations on the disk that this record corresponds to.
3737 CARDINAL32 Cylinder_Count; // The number of cylinders on the drive.
3738 CARDINAL32 Heads_Per_Cylinder; // The number of heads per cylinder for this drive.
3739 CARDINAL32 Sectors_Per_Track; // The number of sectors per track for this drive.
3740 BOOLEAN Drive_Is_PRM; // Set to TRUE if this drive is a PRM.
3741 BYTE Reserved[3]; // Alignment.
3742} Drive_Control_Record;
3743
3744/*
3745 *@@ Drive_Control_Array:
3746 * returned by the Get_Drive_Control_Data function
3747 *
3748 *@@added V0.9.9 (2001-04-07) [umoeller]
3749 */
3750
3751typedef struct _Drive_Control_Array
3752{
3753 Drive_Control_Record * Drive_Control_Data; // An array of drive control records.
3754 CARDINAL32 Count; // The number of entries in the array of drive control records.
3755} Drive_Control_Array;
3756
3757/*
3758 *@@ Drive_Information_Record:
3759 * defines the information that can be changed for a specific disk drive.
3760 *
3761 *@@added V0.9.9 (2001-04-07) [umoeller]
3762 */
3763
3764typedef struct _Drive_Information_Record
3765{
3766 CARDINAL32 Total_Available_Sectors; // The number of sectors on the disk which are not currently assigned to a partition.
3767 CARDINAL32 Largest_Free_Block_Of_Sectors; // The number of sectors in the largest contiguous block of available sectors.
3768 BOOLEAN Corrupt_Partition_Table; // If TRUE, then the partitioning information found on the drive is incorrect!
3769 BOOLEAN Unusable; // If TRUE, the drive's MBR is not accessible and the drive can not be partitioned.
3770 BOOLEAN IO_Error; // If TRUE, then the last I/O operation on this drive failed!
3771 BOOLEAN Is_Big_Floppy; // If TRUE, then the drive is a PRM formatted as a big floppy (i.e. the old style removable media support).
3772 char Drive_Name[DISK_NAME_SIZE]; // User assigned name for this disk drive.
3773} Drive_Information_Record;
3774
3775/*
3776 *@@ Partition_Information_Record:
3777 *
3778 *@@added V0.9.9 (2001-04-07) [umoeller]
3779 */
3780
3781typedef struct _Partition_Information_Record
3782{
3783 ADDRESS Partition_Handle;
3784 // The handle used to perform operations on this partition.
3785 ADDRESS Volume_Handle;
3786 // If this partition is part of a volume, this will be the handle of
3787 // the volume. If this partition is NOT part of a volume, then this
3788 // handle will be 0.
3789 ADDRESS Drive_Handle;
3790 // The handle for the drive this partition resides on.
3791 DoubleWord Partition_Serial_Number;
3792 // The serial number assigned to this partition.
3793 CARDINAL32 Partition_Start;
3794 // The LBA of the first sector of the partition.
3795 CARDINAL32 True_Partition_Size;
3796 // The total number of sectors comprising the partition.
3797 CARDINAL32 Usable_Partition_Size;
3798 // The size of the partition as reported to the IFSM. This is the
3799 // size of the partition less any LVM overhead.
3800 CARDINAL32 Boot_Limit;
3801 // The maximum number of sectors from this block of free space that
3802 // can be used to create a bootable partition if you allocate from the
3803 // beginning of the block of free space.
3804 BOOLEAN Spanned_Volume;
3805 // TRUE if this partition is part of a multi-partition volume.
3806 BOOLEAN Primary_Partition;
3807 // True or False. Any non-zero value here indicates that this partition
3808 // is a primary partition. Zero here indicates that this partition is
3809 // a "logical drive" - i.e. it resides inside of an extended partition.
3810 BYTE Active_Flag;
3811 // 80 = Partition is marked as being active.
3812 // 0 = Partition is not active.
3813 BYTE OS_Flag;
3814 // This field is from the partition table. It is known as the OS flag,
3815 // the Partition Type Field, Filesystem Type, and various other names.
3816 // Values of interest
3817 // If this field is: (values are in hex)
3818 // 07 = The partition is a compatibility partition formatted for use
3819 // with an installable filesystem, such as HPFS or JFS.
3820 // 00 = Unformatted partition
3821 // 01 = FAT12 filesystem is in use on this partition.
3822 // 04 = FAT16 filesystem is in use on this partition.
3823 // 0A = OS/2 Boot Manager Partition
3824 // 35 = LVM partition
3825 // 84 = OS/2 FAT16 partition which has been relabeled by Boot Manager to "Hide" it.
3826 BYTE Partition_Type;
3827 // 0 = Free Space
3828 // 1 = LVM Partition (Part of an LVM Volume.)
3829 // 2 = Compatibility Partition
3830 // All other values are reserved for future use.
3831 BYTE Partition_Status;
3832 // 0 = Free Space
3833 // 1 = In Use - i.e. already assigned to a volume.
3834 // 2 = Available - i.e. not currently assigned to a volume.
3835 BOOLEAN On_Boot_Manager_Menu;
3836 // Set to TRUE if this partition is not part of a Volume yet is on the
3837 // Boot Manager Menu.
3838 BYTE Reserved;
3839 // Alignment.
3840 char Volume_Drive_Letter;
3841 // The drive letter assigned to the volume that this partition is a part of.
3842 char Drive_Name[DISK_NAME_SIZE];
3843 // User assigned name for this disk drive.
3844 char File_System_Name[FILESYSTEM_NAME_SIZE];
3845 // The name of the filesystem in use on this partition, if it is known.
3846 char Partition_Name[PARTITION_NAME_SIZE];
3847 // The user assigned name for this partition.
3848 char Volume_Name[VOLUME_NAME_SIZE];
3849 // If this partition is part of a volume, then this will be the
3850 // name of the volume that this partition is a part of. If this
3851 // record represents free space, then the Volume_Name will be
3852 // "FREE SPACE xx", where xx is a unique numeric ID generated by
3853 // LVM.DLL. Otherwise it will be an empty string.
3854} Partition_Information_Record;
3855
3856// The following defines are for use with the Partition_Type field in the
3857// Partition_Information_Record.
3858#define FREE_SPACE_PARTITION 0
3859#define LVM_PARTITION 1
3860#define COMPATIBILITY_PARTITION 2
3861
3862// The following defines are for use with the Partition_Status field in the
3863// Partition_Information_Record.
3864#define PARTITION_IS_IN_USE 1
3865#define PARTITION_IS_AVAILABLE 2
3866#define PARTITION_IS_FREE_SPACE 0
3867
3868/*
3869 *@@ Partition_Information_Array:
3870 * returned by various functions in the LVM Engine.
3871 *
3872 *@@added V0.9.9 (2001-04-07) [umoeller]
3873 */
3874
3875typedef struct _Partition_Information_Array
3876{
3877 Partition_Information_Record * Partition_Array; // An array of Partition_Information_Records.
3878 CARDINAL32 Count; // The number of entries in the Partition_Array.
3879} Partition_Information_Array;
3880
3881/*
3882 *@@ Volume_Information_Record:
3883 * variable information for a volume.
3884 *
3885 *@@added V0.9.9 (2001-04-07) [umoeller]
3886 */
3887
3888typedef struct _Volume_Information_Record
3889{
3890 CARDINAL32 Volume_Size;
3891 // The number of sectors comprising the volume.
3892 CARDINAL32 Partition_Count;
3893 // The number of partitions which comprise this volume.
3894 CARDINAL32 Drive_Letter_Conflict;
3895 // 0 indicates that the drive letter preference for this volume is unique.
3896 // 1 indicates that the drive letter preference for this volume
3897 // is not unique, but this volume got its preferred drive letter anyway.
3898 // 2 indicates that the drive letter preference for this volume
3899 // is not unique, and this volume did NOT get its preferred drive letter.
3900 // 4 indicates that this volume is currently "hidden" - i.e. it has
3901 // no drive letter preference at the current time.
3902 BOOLEAN Compatibility_Volume;
3903 // TRUE if this is for a compatibility volume, FALSE otherwise.
3904 BOOLEAN Bootable;
3905 // Set to TRUE if this volume appears on the Boot Manager menu, or if it is
3906 // a compatibility volume and its corresponding partition is the first active
3907 // primary partition on the first drive.
3908 char Drive_Letter_Preference;
3909 // The drive letter that this volume desires to be.
3910 char Current_Drive_Letter;
3911 // The drive letter currently used to access this volume.
3912 // May be different than Drive_Letter_Preference if there was a conflict ( i.e. Drive_Letter_Preference
3913 // is already in use by another volume ).
3914 char Initial_Drive_Letter;
3915 // The drive letter assigned to this volume by the operating system
3916 // when LVM was started. This may be different from the
3917 // Drive_Letter_Preference if there were conflicts, and
3918 // may be different from the Current_Drive_Letter. This
3919 // will be 0x0 if the Volume did not exist when the LVM Engine
3920 // was opened (i.e. it was created during this LVM session).
3921 BOOLEAN New_Volume;
3922 // Set to FALSE if this volume existed before the LVM Engine was
3923 // opened. Set to TRUE if this volume was created after the LVM
3924 // Engine was opened.
3925 BYTE Status;
3926 // 0 = None.
3927 // 1 = Bootable
3928 // 2 = Startable
3929 // 3 = Installable.
3930 BYTE Reserved_1;
3931 char Volume_Name[VOLUME_NAME_SIZE];
3932 // The user assigned name for this volume.
3933 char File_System_Name[FILESYSTEM_NAME_SIZE];
3934 // The name of the filesystem in use on this partition, if it
3935 // is known.
3936} Volume_Information_Record;
3937
3938#pragma pack()
3939
3940/********************************************************************
3941 *
3942 * Quick LVM Interface API
3943 *
3944 ********************************************************************/
3945
3946/*
3947 *@@ LVMINFOPRIVATE:
3948 * private structure used by doshQueryLVMInfo.
3949 * This is what the LVMINFO pointer really
3950 * points to.
3951 *
3952 *@@added V0.9.9 (2001-04-07) [umoeller]
3953 */
3954
3955typedef struct _LVMINFOPRIVATE
3956{
3957 LVMINFO LVMInfo; // public structure (dosh.h)
3958
3959 // function pointers resolved from LVM.DLL
3960
3961 void (* _System Open_LVM_Engine)(BOOLEAN Ignore_CHS,
3962 CARDINAL32 *Error_Code);
3963
3964 void (* _System Free_Engine_Memory)(ADDRESS Object);
3965
3966 void (* _System Close_LVM_Engine)(void);
3967
3968 Drive_Control_Array (* _System
3969 Get_Drive_Control_Data)(CARDINAL32 *Error_Code);
3970
3971 Drive_Information_Record (* _System
3972 Get_Drive_Status)(ADDRESS Drive_Handle,
3973 CARDINAL32 *Error_Code);
3974
3975 Partition_Information_Array (* _System
3976 Get_Partitions)(ADDRESS Handle,
3977 CARDINAL32 *Error_Code);
3978
3979 Volume_Information_Record (*_System
3980 Get_Volume_Information)(ADDRESS Volume_Handle,
3981 CARDINAL32 *Error_Code);
3982
3983} LVMINFOPRIVATE, *PLVMINFOPRIVATE;
3984
3985#define LVM_ERROR_FIRST 20000
3986
3987/*
3988 *@@ doshQueryLVMInfo:
3989 * creates an LVMINFO structure if LVM is installed.
3990 * Returns that structure (which the caller must free
3991 * using doshFreeLVMInfo) or NULL if LVM.DLL was not
3992 * found along the LIBPATH.
3993 *
3994 *@@added V0.9.9 (2001-04-07) [umoeller]
3995 */
3996
3997APIRET doshQueryLVMInfo(PLVMINFO *ppLVMInfo)
3998{
3999 APIRET arc = NO_ERROR;
4000 CHAR szError[100];
4001 PLVMINFOPRIVATE pLVMInfo = NULL;
4002 HMODULE hmodLVM = NULLHANDLE;
4003
4004 if (!(arc = DosLoadModule(szError,
4005 sizeof(szError),
4006 "LVM",
4007 &hmodLVM)))
4008 {
4009 // got LVM.DLL:
4010 pLVMInfo = NEW(LVMINFOPRIVATE);
4011 if (!pLVMInfo)
4012 arc = ERROR_NOT_ENOUGH_MEMORY;
4013 else
4014 {
4015 // array of function pointers to be resolved from LVM.DLL
4016 RESOLVEFUNCTION aFunctions[] =
4017 {
4018 "Open_LVM_Engine", (PFN*)&pLVMInfo->Open_LVM_Engine,
4019 "Free_Engine_Memory", (PFN*)&pLVMInfo->Free_Engine_Memory,
4020 "Close_LVM_Engine", (PFN*)&pLVMInfo->Close_LVM_Engine,
4021 "Get_Drive_Control_Data", (PFN*)&pLVMInfo->Get_Drive_Control_Data,
4022 "Get_Drive_Status", (PFN*)&pLVMInfo->Get_Drive_Status,
4023 "Get_Partitions", (PFN*)&pLVMInfo->Get_Partitions,
4024 "Get_Volume_Information", (PFN*)&pLVMInfo->Get_Volume_Information
4025 };
4026 ULONG ul;
4027
4028 ZERO(pLVMInfo);
4029
4030 pLVMInfo->LVMInfo.hmodLVM = hmodLVM;
4031
4032 // now resolve function pointers
4033 for (ul = 0;
4034 ul < ARRAYITEMCOUNT(aFunctions);
4035 ul++)
4036 {
4037 PRESOLVEFUNCTION pFuncThis = &aFunctions[ul];
4038 arc = DosQueryProcAddr(hmodLVM,
4039 0, // ordinal, ignored
4040 (PSZ)pFuncThis->pcszFunctionName,
4041 pFuncThis->ppFuncAddress);
4042 if (!pFuncThis->ppFuncAddress)
4043 arc = ERROR_INVALID_NAME;
4044
4045 if (arc)
4046 break;
4047 }
4048 }
4049 }
4050
4051 if (arc)
4052 doshFreeLVMInfo((PLVMINFO)pLVMInfo);
4053 else
4054 *ppLVMInfo = (PLVMINFO)pLVMInfo;
4055
4056 return (arc);
4057}
4058
4059/*
4060 *@@ doshReadLVMPartitions:
4061 * using the LVMINFO parameter from doshQueryLVMInfo,
4062 * builds an array of PARTITIONINFO structures with
4063 * the data returned from LVM.DLL.
4064 *
4065 *@@added V0.9.9 (2001-04-07) [umoeller]
4066 */
4067
4068APIRET doshReadLVMPartitions(PLVMINFO pInfo, // in: LVM info
4069 PPARTITIONINFO *ppPartitionInfo, // out: partitions array
4070 PUSHORT pcPartitions) // out: partitions count
4071{
4072 APIRET arc = NO_ERROR;
4073 CARDINAL32 Error = 0;
4074
4075 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
4076 *ppiTemp = NULL;
4077 USHORT cPartitions = 0; // bootable partition count
4078 PLVMINFOPRIVATE pLVMInfo = (PLVMINFOPRIVATE)pInfo;
4079
4080 _Pmpf((__FUNCTION__ ": entering"));
4081
4082 if (!pLVMInfo)
4083 return (ERROR_INVALID_PARAMETER);
4084
4085 // initialize LVM engine
4086 pLVMInfo->Open_LVM_Engine(TRUE,
4087 &Error);
4088
4089 _Pmpf((" Open_LVM_Engine Error: %d"));
4090
4091 if (!Error)
4092 {
4093 Drive_Control_Array DCA = pLVMInfo->Get_Drive_Control_Data(&Error);
4094 // member records to be freed
4095
4096 _Pmpf((" Get_Drive_Control_Data Error: %d, drive count: %d", Error, DCA.Count));
4097
4098 if ( (!Error)
4099 && (DCA.Count)
4100 )
4101 {
4102 // DCA.Drive_Control_Data now contains drive information records;
4103 // this must be freed
4104 ULONG ulDisk;
4105
4106 for (ulDisk = 0;
4107 ulDisk < DCA.Count;
4108 ulDisk++)
4109 {
4110 Drive_Control_Record *pDriveControlRecord
4111 = &DCA.Drive_Control_Data[ulDisk];
4112 ADDRESS hDrive = pDriveControlRecord->Drive_Handle;
4113
4114 Drive_Information_Record pDriveInfoRecord
4115 = pLVMInfo->Get_Drive_Status(hDrive,
4116 &Error);
4117
4118 _Pmpf((" drive %d Get_Drive_Status Error: %d", ulDisk, Error));
4119
4120 if (!Error)
4121 {
4122 Partition_Information_Array PIA
4123 = pLVMInfo->Get_Partitions(hDrive,
4124 &Error);
4125
4126 _Pmpf((" Get_Partitions Error: %d", Error));
4127
4128 if (!Error)
4129 {
4130 // PIA.Partition_Array now contains
4131 // Partition_Information_Record; must be freed
4132
4133 // now go thru partitions of this drive
4134 ULONG ulPart;
4135 for (ulPart = 0;
4136 ulPart < PIA.Count;
4137 ulPart++)
4138 {
4139 Partition_Information_Record *pPartition
4140 = &PIA.Partition_Array[ulPart];
4141 Volume_Information_Record VolumeInfo;
4142
4143 const char *pcszBootName = NULL; // for now
4144 BOOL fBootable = FALSE;
4145
4146 if (pPartition->Volume_Handle)
4147 {
4148 // this partition is part of a volume:
4149 // only then can it be bootable...
4150 // get the volume info
4151 VolumeInfo
4152 = pLVMInfo->Get_Volume_Information(pPartition->Volume_Handle,
4153 &Error);
4154 pcszBootName = VolumeInfo.Volume_Name;
4155
4156 fBootable = (VolumeInfo.Status == 1);
4157 }
4158
4159
4160 if (arc = AppendPartition(&pPartitionInfos,
4161 &ppiTemp,
4162 &cPartitions,
4163 ulDisk + 1,
4164 pcszBootName,
4165 pPartition->Volume_Drive_Letter,
4166 pPartition->OS_Flag, // FS type
4167 pPartition->Primary_Partition,
4168 fBootable,
4169 pPartition->True_Partition_Size))
4170 break;
4171 }
4172
4173 // clean up partitions
4174 pLVMInfo->Free_Engine_Memory(PIA.Partition_Array);
4175 }
4176 }
4177 else
4178 // error:
4179 break;
4180 }
4181
4182 // clean up drive data
4183 pLVMInfo->Free_Engine_Memory(DCA.Drive_Control_Data);
4184 }
4185 }
4186
4187 // close LVM
4188 pLVMInfo->Close_LVM_Engine();
4189
4190 if (Error)
4191 {
4192 // if we got an error, return it with the
4193 // LVM error offset
4194 arc = LVM_ERROR_FIRST + Error;
4195
4196 CleanPartitionInfos(pPartitionInfos);
4197 }
4198
4199 if (!arc)
4200 {
4201 *ppPartitionInfo = pPartitionInfos;
4202 *pcPartitions = cPartitions;
4203 }
4204
4205 _Pmpf((__FUNCTION__ ": exiting, arg = %d", arc));
4206
4207 return (arc);
4208}
4209
4210/*
4211 *@@ doshFreeLVMInfo:
4212 *
4213 *@@added V0.9.9 (2001-04-07) [umoeller]
4214 */
4215
4216VOID doshFreeLVMInfo(PLVMINFO pInfo)
4217{
4218 if (pInfo)
4219 {
4220 if (pInfo->hmodLVM)
4221 DosFreeModule(pInfo->hmodLVM);
4222
4223 free(pInfo);
4224 }
4225}
Note: See TracBrowser for help on using the repository browser.