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

Last change on this file since 65 was 64, checked in by umoeller, 24 years ago

Sources as for V0.9.11.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 141.7 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: specified file
800 * has no DOS EXE header, or it does, but
801 * the extended header is neither LX nor
802 * NE nor PE.
803 *
804 * -- ERROR_INVALID_PARAMETER: ppExec is NULL.
805 *
806 * plus those of DosOpen, DosSetFilePtr, and
807 * DosRead.
808 *
809 *@@added V0.9.0 [umoeller]
810 *@@changed V0.9.1 (2000-02-13) [umoeller]: fixed 32-bits flag
811 *@@changed V0.9.7 (2000-12-20) [lafaix]: fixed ulNewHeaderOfs
812 *@@changed V0.9.10 (2001-04-08) [lafaix]: added PE support
813 *@@changed V0.9.10 (2001-04-08) [umoeller]: now setting ppExec only if NO_ERROR is returned
814 */
815
816APIRET doshExecOpen(const char* pcszExecutable,
817 PEXECUTABLE* ppExec)
818{
819 APIRET arc = NO_ERROR;
820
821 ULONG ulAction = 0;
822 HFILE hFile;
823 PEXECUTABLE pExec = NULL;
824
825 if (!ppExec)
826 return (ERROR_INVALID_PARAMETER);
827
828 pExec = (PEXECUTABLE)malloc(sizeof(EXECUTABLE));
829 if (!(pExec))
830 return (ERROR_NOT_ENOUGH_MEMORY);
831
832 memset(pExec, 0, sizeof(EXECUTABLE));
833
834 if (!(arc = DosOpen((PSZ)pcszExecutable,
835 &hFile,
836 &ulAction, // out: action taken
837 0, // in: new file (ignored for read-mode)
838 0, // in: new file attribs (ignored)
839 // open-flags
840 OPEN_ACTION_FAIL_IF_NEW
841 | OPEN_ACTION_OPEN_IF_EXISTS,
842 // open-mode
843 OPEN_FLAGS_FAIL_ON_ERROR // report errors to caller
844 | OPEN_FLAGS_SEQUENTIAL
845 | OPEN_FLAGS_NOINHERIT
846 | OPEN_SHARE_DENYNONE
847 | OPEN_ACCESS_READONLY, // read-only mode
848 NULL))) // no EAs
849 {
850 // file opened successfully:
851
852 ULONG ulLocal = 0;
853
854 // read old DOS EXE header
855 pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
856 if (!(pExec->pDosExeHeader))
857 arc = ERROR_NOT_ENOUGH_MEMORY;
858 else
859 {
860 ULONG ulBytesRead = 0;
861
862 if ( (!(arc = DosSetFilePtr(hFile,
863 0L,
864 FILE_BEGIN,
865 &ulLocal))) // out: new offset
866 && (!(arc = DosRead(hFile,
867 pExec->pDosExeHeader,
868 sizeof(DOSEXEHEADER),
869 &(pExec->cbDosExeHeader))))
870 )
871 {
872 // now check if we really have a DOS header
873 if (pExec->pDosExeHeader->usDosExeID != 0x5a4d)
874 arc = ERROR_INVALID_EXE_SIGNATURE;
875 else
876 {
877 // we have a DOS header:
878 if (pExec->pDosExeHeader->usRelocTableOfs < 0x40)
879 {
880 // neither LX nor PE nor NE:
881 pExec->ulOS = EXEOS_DOS3;
882 pExec->ulExeFormat = EXEFORMAT_OLDDOS;
883 }
884 else
885 {
886 // either LX or PE or NE:
887 // read more bytes from position specified in header
888 CHAR achNewHeaderType[2] = "";
889
890 if ( (!(arc = DosSetFilePtr(hFile,
891 pExec->pDosExeHeader->ulNewHeaderOfs,
892 FILE_BEGIN,
893 &ulLocal)))
894 // read two chars to find out header type
895 && (!(arc = DosRead(hFile,
896 &achNewHeaderType,
897 sizeof(achNewHeaderType),
898 &ulBytesRead)))
899 )
900 {
901 PBYTE pbCheckOS = NULL;
902
903 // reset file ptr
904 DosSetFilePtr(hFile,
905 pExec->pDosExeHeader->ulNewHeaderOfs,
906 FILE_BEGIN,
907 &ulLocal);
908
909 if (memcmp(achNewHeaderType, "NE", 2) == 0)
910 {
911 // New Executable:
912 pExec->ulExeFormat = EXEFORMAT_NE;
913 // allocate NE header
914 pExec->pNEHeader = (PNEHEADER)malloc(sizeof(NEHEADER));
915 if (!(pExec->pNEHeader))
916 arc = ERROR_NOT_ENOUGH_MEMORY;
917 else
918 // read in NE header
919 if (!(arc = DosRead(hFile,
920 pExec->pNEHeader,
921 sizeof(NEHEADER),
922 &(pExec->cbNEHeader))))
923 if (pExec->cbNEHeader == sizeof(NEHEADER))
924 pbCheckOS = &(pExec->pNEHeader->bTargetOS);
925 }
926 else if ( (memcmp(achNewHeaderType, "LX", 2) == 0)
927 || (memcmp(achNewHeaderType, "LE", 2) == 0)
928 // this is used by SMARTDRV.EXE
929 )
930 {
931 // OS/2 Linear Executable:
932 pExec->ulExeFormat = EXEFORMAT_LX;
933 // allocate LX header
934 pExec->pLXHeader = (PLXHEADER)malloc(sizeof(LXHEADER));
935 if (!(pExec->pLXHeader))
936 arc = ERROR_NOT_ENOUGH_MEMORY;
937 else
938 // read in LX header
939 if (!(arc = DosRead(hFile,
940 pExec->pLXHeader,
941 sizeof(LXHEADER),
942 &(pExec->cbLXHeader))))
943 if (pExec->cbLXHeader == sizeof(LXHEADER))
944 pbCheckOS = (PBYTE)(&(pExec->pLXHeader->usTargetOS));
945 }
946 else if (memcmp(achNewHeaderType, "PE", 2) == 0)
947 {
948 PEHEADER PEHeader = {0};
949
950 pExec->ulExeFormat = EXEFORMAT_PE;
951 pExec->ulOS = EXEOS_WIN32;
952 pExec->f32Bits = TRUE;
953
954 // V0.9.10 (2001-04-08) [lafaix]
955 // read in standard PE header
956 if (!(arc = DosRead(hFile,
957 &PEHeader,
958 24,
959 &ulBytesRead)))
960 {
961 // allocate PE header
962 pExec->pPEHeader = (PPEHEADER)malloc(24 + PEHeader.usHeaderSize);
963 if (!(pExec->pPEHeader))
964 arc = ERROR_NOT_ENOUGH_MEMORY;
965 else
966 {
967 // copy standard PE header
968 memcpy(pExec->pPEHeader,
969 &PEHeader,
970 24);
971
972 // read in optional PE header
973 if (!(arc = DosRead(hFile,
974 &(pExec->pPEHeader->usReserved3),
975 PEHeader.usHeaderSize,
976 &(pExec->cbPEHeader))))
977 pExec->cbPEHeader += 24;
978 }
979 }
980 }
981 else
982 // strange type:
983 arc = ERROR_INVALID_EXE_SIGNATURE;
984
985 if (pbCheckOS)
986 // BYTE to check for operating system
987 // (NE and LX):
988 switch (*pbCheckOS)
989 {
990 case NEOS_OS2:
991 pExec->ulOS = EXEOS_OS2;
992 if (pExec->ulExeFormat == EXEFORMAT_LX)
993 pExec->f32Bits = TRUE;
994 break;
995
996 case NEOS_WIN16:
997 pExec->ulOS = EXEOS_WIN16;
998 break;
999
1000 case NEOS_DOS4:
1001 pExec->ulOS = EXEOS_DOS4;
1002 break;
1003
1004 case NEOS_WIN386:
1005 pExec->ulOS = EXEOS_WIN386;
1006 pExec->f32Bits = TRUE;
1007 break;
1008 }
1009 } // end if (!(arc = DosSetFilePtr(hFile,
1010 }
1011 }
1012 } // end if (!(arc = DosSetFilePtr(hFile,
1013 } // end if pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER));
1014
1015 // store exec's HFILE
1016 pExec->hfExe = hFile;
1017 } // end if (!(arc = DosOpen((PSZ)pcszExecutable,
1018
1019 if (arc != NO_ERROR)
1020 // error: clean up
1021 doshExecClose(pExec);
1022 else
1023 *ppExec = pExec;
1024
1025 return (arc);
1026}
1027
1028/*
1029 *@@ doshExecClose:
1030 * this closes an executable opened with doshExecOpen.
1031 * Always call this function if NO_ERROR was returned by
1032 * doshExecOpen.
1033 *
1034 *@@added V0.9.0 [umoeller]
1035 */
1036
1037APIRET doshExecClose(PEXECUTABLE pExec)
1038{
1039 APIRET arc = NO_ERROR;
1040 if (pExec)
1041 {
1042 if (pExec->pDosExeHeader)
1043 free(pExec->pDosExeHeader);
1044 if (pExec->pNEHeader)
1045 free(pExec->pNEHeader);
1046 if (pExec->pLXHeader)
1047 free(pExec->pLXHeader);
1048
1049 if (pExec->pszDescription)
1050 free(pExec->pszDescription);
1051 if (pExec->pszVendor)
1052 free(pExec->pszVendor);
1053 if (pExec->pszVersion)
1054 free(pExec->pszVersion);
1055 if (pExec->pszInfo)
1056 free(pExec->pszInfo);
1057
1058 if (pExec->hfExe)
1059 arc = DosClose(pExec->hfExe);
1060
1061 free(pExec);
1062 }
1063 else
1064 arc = ERROR_INVALID_PARAMETER;
1065
1066 return (arc);
1067}
1068
1069/*
1070 *@@ doshExecQueryBldLevel:
1071 * this retrieves buildlevel information for an
1072 * LX or NE executable previously opened with
1073 * doshExecOpen.
1074 *
1075 * BuildLevel information must be contained in the
1076 * DESCRIPTION field of an executable's module
1077 * definition (.DEF) file. In order to be readable
1078 * by BLDLEVEL.EXE (which ships with OS/2), this
1079 * string must have the following format:
1080 *
1081 + Description '@#AUTHOR:VERSION#@ DESCRIPTION'
1082 *
1083 * Example:
1084 *
1085 + Description '@#Ulrich M”ller:0.9.0#@ XWorkplace Sound Support Module'
1086 *
1087 * The "Description" entry always ends up as the
1088 * very first entry in the non-resident name table
1089 * in LX and NE executables. So this is what we retrieve
1090 * here.
1091 *
1092 * If the first entry in that table exists, NO_ERROR is
1093 * returned and at least the pszDescription field in
1094 * EXECUTABLE is set to that information.
1095 *
1096 * If that string is in IBM BLDLEVEL format, the string
1097 * is automatically parsed, and the pszVendor, pszVersion,
1098 * and pszInfo fields are also set. In the above examples,
1099 * this would return the following information:
1100 + pszVendor = "Ulrich M”ller"
1101 + pszVersion = "0.9.0"
1102 + pszInfo = "XWorkplace Sound Support Module"
1103 *
1104 * If that string is not in BLDLEVEL format, only pszDescription
1105 * will be set. The other fields remain NULL.
1106 *
1107 * This returns the following errors:
1108 *
1109 * -- ERROR_INVALID_PARAMETER: pExec invalid
1110 *
1111 * -- ERROR_INVALID_EXE_SIGNATURE (191): pExec is not in LX or NE format
1112 *
1113 * -- ERROR_INVALID_DATA (13): non-resident name table not found.
1114 *
1115 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
1116 *
1117 * plus the error codes of DosSetFilePtr and DosRead.
1118 *
1119 *@@added V0.9.0 [umoeller]
1120 *@@changed V0.9.0 (99-10-22) [umoeller]: NE format now supported
1121 *@@changed V0.9.1 (99-12-06): fixed memory leak
1122 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
1123 */
1124
1125APIRET doshExecQueryBldLevel(PEXECUTABLE pExec)
1126{
1127 APIRET arc = NO_ERROR;
1128
1129 if (!pExec)
1130 arc = ERROR_INVALID_PARAMETER;
1131 else
1132 {
1133 ULONG ulNRNTOfs = 0;
1134
1135 if (pExec->ulExeFormat == EXEFORMAT_LX)
1136 {
1137 // OK, LX format:
1138 // check if we have a non-resident name table
1139 if (pExec->pLXHeader == NULL)
1140 arc = ERROR_INVALID_DATA;
1141 else if (pExec->pLXHeader->ulNonResdNameTblOfs == 0)
1142 arc = ERROR_INVALID_DATA;
1143 else
1144 ulNRNTOfs = pExec->pLXHeader->ulNonResdNameTblOfs;
1145 }
1146 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1147 {
1148 // OK, NE format:
1149 // check if we have a non-resident name table
1150 if (pExec->pNEHeader == NULL)
1151 arc = ERROR_INVALID_DATA;
1152 else if (pExec->pNEHeader->ulNonResdTblOfs == 0)
1153 arc = ERROR_INVALID_DATA;
1154 else
1155 ulNRNTOfs = pExec->pNEHeader->ulNonResdTblOfs;
1156 }
1157 else
1158 // neither LX nor NE: stop
1159 arc = ERROR_INVALID_EXE_SIGNATURE;
1160
1161 if ( (!arc)
1162 && (ulNRNTOfs)
1163 )
1164 {
1165 ULONG ulLocal = 0,
1166 ulBytesRead = 0;
1167
1168 // move EXE file pointer to offset of non-resident name table
1169 // (from LX header)
1170 if (!(arc = DosSetFilePtr(pExec->hfExe, // file is still open
1171 ulNRNTOfs, // ofs determined above
1172 FILE_BEGIN,
1173 &ulLocal)))
1174 {
1175 // allocate memory as necessary
1176 PSZ pszNameTable = (PSZ)malloc(2001); // should suffice, because each entry
1177 // may only be 255 bytes in length
1178 if (!pszNameTable)
1179 arc = ERROR_NOT_ENOUGH_MEMORY;
1180 else
1181 {
1182 if (!(arc = DosRead(pExec->hfExe,
1183 pszNameTable,
1184 2000,
1185 &ulBytesRead)))
1186 {
1187 if (*pszNameTable == 0)
1188 // first byte (length byte) is null:
1189 arc = ERROR_INVALID_DATA;
1190 else
1191 {
1192 // now copy the string, which is in Pascal format
1193 pExec->pszDescription = (PSZ)malloc((*pszNameTable) + 1); // addt'l null byte
1194 if (!pExec->pszDescription)
1195 arc = ERROR_NOT_ENOUGH_MEMORY;
1196 else
1197 {
1198 const char *pStartOfAuthor = 0,
1199 *pStartOfVendor = 0;
1200
1201 memcpy(pExec->pszDescription,
1202 pszNameTable + 1, // skip length byte
1203 *pszNameTable); // length byte
1204 // terminate string
1205 *(pExec->pszDescription + (*pszNameTable)) = 0;
1206
1207 // now parse the damn thing:
1208 // @#VENDOR:VERSION#@ DESCRIPTION
1209 // but skip the first byte, which has the string length
1210 pStartOfVendor = strstr(pExec->pszDescription,
1211 "@#");
1212 if (pStartOfVendor)
1213 {
1214 const char *pStartOfInfo = strstr(pStartOfVendor + 2,
1215 "#@");
1216 if (pStartOfInfo)
1217 {
1218 PSZ pEndOfVendor = strchr(pStartOfVendor + 2,
1219 ':');
1220 if (pEndOfVendor)
1221 {
1222 pExec->pszVendor = strhSubstr(pStartOfVendor + 2,
1223 pEndOfVendor);
1224 pExec->pszVersion = strhSubstr(pEndOfVendor + 1,
1225 pStartOfInfo);
1226 // skip "@#" in info string
1227 pStartOfInfo += 2;
1228 // skip leading spaces in info string
1229 while (*pStartOfInfo == ' ')
1230 pStartOfInfo++;
1231 if (*pStartOfInfo) // V0.9.9 (2001-04-04) [umoeller]
1232 // and copy until end of string
1233 pExec->pszInfo = strdup(pStartOfInfo);
1234 }
1235 }
1236 }
1237 }
1238 }
1239 }
1240
1241 free(pszNameTable);
1242 } // end if PSZ pszNameTable = (PSZ)malloc(2001);
1243 }
1244 }
1245 } // end if (!pExec)
1246
1247 return (arc);
1248}
1249
1250/*
1251 *@@ doshExecQueryImportedModules:
1252 * returns an array of FSYSMODULE structure describing all
1253 * imported modules.
1254 *
1255 * *pcModules receives the # of items in the array (not the
1256 * array size!). Use doshFreeImportedModules to clean up.
1257 *
1258 * This returns a standard OS/2 error code, which might be
1259 * any of the codes returned by DosSetFilePtr and DosRead.
1260 * In addition, this may return:
1261 *
1262 * -- ERROR_NOT_ENOUGH_MEMORY
1263 *
1264 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1265 * than LX or NE, which is not understood by this function.
1266 *
1267 * Even if NO_ERROR is returned, the array pointer might still
1268 * be NULL if the module contains no such data.
1269 *
1270 *@@added V0.9.9 (2001-03-11) [lafaix]
1271 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1272 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1273 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
1274 *@@changed V0.9.10 (2001-04-13) [lafaix]: removed 127 characters limit
1275 */
1276
1277APIRET doshExecQueryImportedModules(PEXECUTABLE pExec,
1278 PFSYSMODULE *ppaModules, // out: modules array
1279 PULONG pcModules) // out: array item count
1280{
1281 if ( (pExec)
1282 && ( (pExec->ulOS == EXEOS_OS2)
1283 || (pExec->ulOS == EXEOS_WIN16)
1284 || (pExec->ulOS == EXEOS_WIN386)
1285 )
1286 )
1287 {
1288 ENSURE_BEGIN;
1289 ULONG cModules = 0;
1290 PFSYSMODULE paModules = NULL;
1291 int i;
1292
1293 if (pExec->ulExeFormat == EXEFORMAT_LX)
1294 {
1295 // 32-bit OS/2 executable:
1296 cModules = pExec->pLXHeader->ulImportModTblCnt;
1297
1298 if (cModules)
1299 {
1300 ULONG cb = sizeof(FSYSMODULE) * cModules; // V0.9.9 (2001-04-03) [umoeller]
1301 ULONG ulDummy;
1302
1303 paModules = (PFSYSMODULE)malloc(cb);
1304 if (!paModules)
1305 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
1306
1307 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1308
1309 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1310 pExec->pLXHeader->ulImportModTblOfs
1311 + pExec->pDosExeHeader->ulNewHeaderOfs,
1312 FILE_BEGIN,
1313 &ulDummy));
1314
1315 for (i = 0; i < cModules; i++)
1316 {
1317 BYTE bLen = 0;
1318
1319 // reading the length of the module name
1320 ENSURE_SAFE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1321
1322 // reading the module name
1323 ENSURE_SAFE(DosRead(pExec->hfExe,
1324 paModules[i].achModuleName,
1325 bLen,
1326 &ulDummy));
1327
1328 // module names are not null terminated, so we must
1329 // do it now
1330 paModules[i].achModuleName[bLen] = 0;
1331 } // end for
1332 }
1333 } // end LX
1334 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1335 {
1336 // 16-bit executable:
1337 cModules = pExec->pNEHeader->usModuleTblEntries;
1338
1339 if (cModules)
1340 {
1341 ULONG cb = sizeof(FSYSMODULE) * cModules;
1342
1343 paModules = (PFSYSMODULE)malloc(cb);
1344 if (!paModules)
1345 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
1346
1347 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1348
1349 for (i = 0; i < cModules; i ++)
1350 {
1351 BYTE bLen;
1352 USHORT usOfs;
1353 ULONG ulDummy;
1354
1355 // the module reference table contains offsets
1356 // relative to the import table; we hence read
1357 // the offset in the module reference table, and
1358 // then we read the name in the import table
1359
1360 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1361 pExec->pNEHeader->usModRefTblOfs
1362 + pExec->pDosExeHeader->ulNewHeaderOfs
1363 + sizeof(usOfs) * i,
1364 FILE_BEGIN,
1365 &ulDummy));
1366
1367 ENSURE_SAFE(DosRead(pExec->hfExe, &usOfs, 2, &ulDummy));
1368
1369 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1370 pExec->pNEHeader->usImportTblOfs
1371 + pExec->pDosExeHeader->ulNewHeaderOfs
1372 + usOfs,
1373 FILE_BEGIN,
1374 &ulDummy));
1375
1376 ENSURE_SAFE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1377
1378 ENSURE_SAFE(DosRead(pExec->hfExe,
1379 paModules[i].achModuleName,
1380 bLen,
1381 &ulDummy));
1382
1383 paModules[i].achModuleName[bLen] = 0;
1384 } // end for
1385 }
1386 }
1387 else
1388 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1389
1390 // no error: output data
1391 *ppaModules = paModules;
1392 *pcModules = cModules;
1393
1394 ENSURE_FINALLY;
1395 // if we had an error above, clean up
1396 free(paModules);
1397 ENSURE_END;
1398 }
1399 else
1400 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1401
1402 ENSURE_OK;
1403}
1404
1405/*
1406 *@@ doshExecFreeImportedModules:
1407 * frees resources allocated by doshExecQueryImportedModules.
1408 *
1409 *@@added V0.9.9 (2001-03-11)
1410 */
1411
1412APIRET doshExecFreeImportedModules(PFSYSMODULE paModules)
1413{
1414 free(paModules);
1415 return (NO_ERROR);
1416}
1417
1418/*
1419 *@@ ScanLXEntryTable:
1420 * returns the number of exported entries in the entry table.
1421 *
1422 * If paFunctions is not NULL, then successive entries are
1423 * filled with the found type and ordinal values.
1424 *
1425 *@@added V0.9.9 (2001-03-30) [lafaix]
1426 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1427 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1428 */
1429
1430APIRET ScanLXEntryTable(PEXECUTABLE pExec,
1431 PFSYSFUNCTION paFunctions,
1432 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1433{
1434 ULONG ulDummy;
1435 USHORT usOrdinal = 1,
1436 usCurrent = 0;
1437 int i;
1438
1439 ENSURE(DosSetFilePtr(pExec->hfExe,
1440 pExec->pLXHeader->ulEntryTblOfs
1441 + pExec->pDosExeHeader->ulNewHeaderOfs,
1442 FILE_BEGIN,
1443 &ulDummy));
1444
1445 while (TRUE)
1446 {
1447 BYTE bCnt,
1448 bType,
1449 bFlag;
1450
1451 ENSURE(DosRead(pExec->hfExe, &bCnt, 1, &ulDummy));
1452
1453 if (bCnt == 0)
1454 // end of the entry table
1455 break;
1456
1457 ENSURE(DosRead(pExec->hfExe, &bType, 1, &ulDummy));
1458
1459 switch (bType & 0x7F)
1460 {
1461 /*
1462 * unused entries
1463 *
1464 */
1465
1466 case 0:
1467 usOrdinal += bCnt;
1468 break;
1469
1470 /*
1471 * 16-bit entries
1472 *
1473 * the bundle type is followed by the object number
1474 * and by bCnt bFlag+usOffset entries
1475 *
1476 */
1477
1478 case 1:
1479 ENSURE(DosSetFilePtr(pExec->hfExe,
1480 sizeof(USHORT),
1481 FILE_CURRENT,
1482 &ulDummy));
1483
1484 for (i = 0; i < bCnt; i ++)
1485 {
1486 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1487
1488 if (bFlag & 0x01)
1489 {
1490 if (paFunctions)
1491 {
1492 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1493 paFunctions[usCurrent].ulType = 1;
1494 paFunctions[usCurrent].achFunctionName[0] = 0;
1495 }
1496 usCurrent++;
1497 }
1498
1499 usOrdinal++;
1500
1501 ENSURE(DosSetFilePtr(pExec->hfExe,
1502 sizeof(USHORT),
1503 FILE_CURRENT,
1504 &ulDummy));
1505
1506 } // end for
1507 break;
1508
1509 /*
1510 * 286 call gate entries
1511 *
1512 * the bundle type is followed by the object number
1513 * and by bCnt bFlag+usOffset+usCallGate entries
1514 *
1515 */
1516
1517 case 2:
1518 ENSURE(DosSetFilePtr(pExec->hfExe,
1519 sizeof(USHORT),
1520 FILE_CURRENT,
1521 &ulDummy));
1522
1523 for (i = 0; i < bCnt; i ++)
1524 {
1525 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1526
1527 if (bFlag & 0x01)
1528 {
1529 if (paFunctions)
1530 {
1531 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1532 paFunctions[usCurrent].ulType = 2;
1533 paFunctions[usCurrent].achFunctionName[0] = 0;
1534 }
1535 usCurrent++;
1536 }
1537
1538 usOrdinal++;
1539
1540 ENSURE(DosSetFilePtr(pExec->hfExe,
1541 sizeof(USHORT) + sizeof(USHORT),
1542 FILE_CURRENT,
1543 &ulDummy));
1544
1545 } // end for
1546 break;
1547
1548 /*
1549 * 32-bit entries
1550 *
1551 * the bundle type is followed by the object number
1552 * and by bCnt bFlag+ulOffset entries
1553 *
1554 */
1555
1556 case 3:
1557 ENSURE(DosSetFilePtr(pExec->hfExe,
1558 sizeof(USHORT),
1559 FILE_CURRENT,
1560 &ulDummy));
1561
1562 for (i = 0; i < bCnt; i ++)
1563 {
1564 ENSURE(DosRead(pExec->hfExe, &bFlag, 1, &ulDummy));
1565
1566 if (bFlag & 0x01)
1567 {
1568 if (paFunctions)
1569 {
1570 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1571 paFunctions[usCurrent].ulType = 3;
1572 paFunctions[usCurrent].achFunctionName[0] = 0;
1573 }
1574 usCurrent++;
1575 }
1576
1577 usOrdinal++;
1578
1579 ENSURE(DosSetFilePtr(pExec->hfExe,
1580 sizeof(ULONG),
1581 FILE_CURRENT,
1582 &ulDummy));
1583 } // end for
1584 break;
1585
1586 /*
1587 * forwarder entries
1588 *
1589 * the bundle type is followed by a reserved word
1590 * and by bCnt bFlag+usModOrd+ulOffsOrdNum entries
1591 *
1592 */
1593
1594 case 4:
1595 ENSURE(DosSetFilePtr(pExec->hfExe,
1596 sizeof(USHORT),
1597 FILE_CURRENT,
1598 &ulDummy));
1599
1600 for (i = 0; i < bCnt; i ++)
1601 {
1602 ENSURE(DosSetFilePtr(pExec->hfExe,
1603 sizeof(BYTE) + sizeof(USHORT) + sizeof(ULONG),
1604 FILE_CURRENT,
1605 &ulDummy));
1606
1607 if (paFunctions)
1608 {
1609 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1610 paFunctions[usCurrent].ulType = 4;
1611 paFunctions[usCurrent].achFunctionName[0] = 0;
1612 }
1613 usCurrent++;
1614
1615 usOrdinal++;
1616 } // end for
1617 break;
1618
1619 /*
1620 * unknown bundle type
1621 *
1622 * we don't know how to handle this bundle, so we must
1623 * stop parsing the entry table here (as we don't know the
1624 * bundle size); if paFunctions is not null, we fill it with
1625 * informative data
1626 */
1627
1628 default:
1629 if (paFunctions)
1630 {
1631 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1632 paFunctions[usCurrent].ulType = bType;
1633 sprintf(paFunctions[usCurrent].achFunctionName,
1634 "Unknown bundle type encountered (%d). Aborting entry table scan.",
1635 bType);
1636
1637 usCurrent++;
1638 }
1639 ENSURE_FAIL(ERROR_INVALID_LIST_FORMAT);
1640 // whatever
1641 // V0.9.9 (2001-04-03) [umoeller]
1642 } // end switch (bType & 0x7F)
1643 } // end while (TRUE)
1644
1645 if (pcEntries)
1646 *pcEntries = usCurrent;
1647
1648 ENSURE_OK;
1649}
1650
1651/*
1652 *@@ ScanNEEntryTable:
1653 * returns the number of exported entries in the entry table.
1654 *
1655 * if paFunctions is not NULL, then successive entries are
1656 * filled with the found type and ordinal values.
1657 *
1658 *@@added V0.9.9 (2001-03-30) [lafaix]
1659 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1660 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1661 */
1662
1663APIRET ScanNEEntryTable(PEXECUTABLE pExec,
1664 PFSYSFUNCTION paFunctions,
1665 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1666{
1667 ULONG ulDummy;
1668 USHORT usOrdinal = 1,
1669 usCurrent = 0;
1670 int i;
1671
1672 ENSURE(DosSetFilePtr(pExec->hfExe,
1673 pExec->pNEHeader->usEntryTblOfs
1674 + pExec->pDosExeHeader->ulNewHeaderOfs,
1675 FILE_BEGIN,
1676 &ulDummy));
1677
1678 while (TRUE)
1679 {
1680 BYTE bCnt,
1681 bType,
1682 bFlag;
1683
1684 ENSURE(DosRead(pExec->hfExe, &bCnt, 1, &ulDummy));
1685
1686 if (bCnt == 0)
1687 // end of the entry table
1688 break;
1689
1690 ENSURE(DosRead(pExec->hfExe, &bType, 1, &ulDummy));
1691
1692 if (bType)
1693 {
1694 for (i = 0; i < bCnt; i++)
1695 {
1696 ENSURE(DosRead(pExec->hfExe,
1697 &bFlag,
1698 1,
1699 &ulDummy));
1700
1701 if (bFlag & 0x01)
1702 {
1703 if (paFunctions)
1704 {
1705 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1706 paFunctions[usCurrent].ulType = 1; // 16-bit entry
1707 paFunctions[usCurrent].achFunctionName[0] = 0;
1708 }
1709 usCurrent++;
1710 }
1711
1712 usOrdinal++;
1713
1714 if (bType == 0xFF)
1715 {
1716 // moveable segment
1717 ENSURE(DosSetFilePtr(pExec->hfExe,
1718 5,
1719 FILE_CURRENT,
1720 &ulDummy));
1721 }
1722 else
1723 {
1724 // fixed segment or constant (0xFE)
1725 ENSURE(DosSetFilePtr(pExec->hfExe,
1726 2,
1727 FILE_CURRENT,
1728 &ulDummy));
1729 }
1730
1731 } // end for
1732 }
1733 else
1734 usOrdinal += bCnt;
1735 } // end while (TRUE)
1736
1737 if (pcEntries)
1738 *pcEntries = usCurrent;
1739
1740 ENSURE_OK;
1741}
1742
1743/*
1744 *@@ Compare:
1745 * binary search helper
1746 *
1747 *@@added V0.9.9 (2001-04-01) [lafaix]
1748 *@@changed V0.9.9 (2001-04-07) [umoeller]: added _Optlink, or this won't compile as C++
1749 */
1750
1751int _Optlink Compare(const void *key,
1752 const void *element)
1753{
1754 USHORT usOrdinal = *((PUSHORT) key);
1755 PFSYSFUNCTION pFunction = (PFSYSFUNCTION)element;
1756
1757 if (usOrdinal > pFunction->ulOrdinal)
1758 return (1);
1759 else if (usOrdinal < pFunction->ulOrdinal)
1760 return (-1);
1761 else
1762 return (0);
1763}
1764
1765/*
1766 *@@ ScanNameTable:
1767 * scans a resident or non-resident name table, and fills the
1768 * appropriate paFunctions entries when it encounters exported
1769 * entries names.
1770 *
1771 * This functions works for both NE and LX executables.
1772 *
1773 *@@added V0.9.9 (2001-03-30) [lafaix]
1774 *@@changed V0.9.9 (2001-04-02) [lafaix]: the first entry is special
1775 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1776 *@@changed V0.9.9 (2001-04-05) [lafaix]: removed the 127 char limit
1777 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1778 */
1779
1780APIRET ScanNameTable(PEXECUTABLE pExec,
1781 ULONG cFunctions,
1782 PFSYSFUNCTION paFunctions)
1783{
1784 ULONG ulDummy;
1785
1786 USHORT usOrdinal;
1787 PFSYSFUNCTION pFunction;
1788
1789 while (TRUE)
1790 {
1791 BYTE bLen;
1792 CHAR achName[256];
1793 int i;
1794
1795 ENSURE(DosRead(pExec->hfExe, &bLen, 1, &ulDummy));
1796
1797 if (bLen == 0)
1798 // end of the name table
1799 break;
1800
1801 ENSURE(DosRead(pExec->hfExe, &achName, bLen, &ulDummy));
1802 achName[bLen] = 0;
1803
1804 ENSURE(DosRead(pExec->hfExe, &usOrdinal, sizeof(USHORT), &ulDummy));
1805
1806 if ((pFunction = (PFSYSFUNCTION)bsearch(&usOrdinal,
1807 paFunctions,
1808 cFunctions,
1809 sizeof(FSYSFUNCTION),
1810 Compare)))
1811 {
1812 memcpy(pFunction->achFunctionName,
1813 achName,
1814 bLen+1);
1815 }
1816 }
1817
1818 ENSURE_OK;
1819}
1820
1821/*
1822 *@@ doshExecQueryExportedFunctions:
1823 * returns an array of FSYSFUNCTION structure describing all
1824 * exported functions.
1825 *
1826 * *pcFunctions receives the # of items in the array (not the
1827 * array size!). Use doshFreeExportedFunctions to clean up.
1828 *
1829 * Note that the returned array only contains entry for exported
1830 * functions. Empty export entries are _not_ included.
1831 *
1832 * This returns a standard OS/2 error code, which might be
1833 * any of the codes returned by DosSetFilePtr and DosRead.
1834 * In addition, this may return:
1835 *
1836 * -- ERROR_NOT_ENOUGH_MEMORY
1837 *
1838 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1839 * than LX or NE, which is not understood by this function.
1840 *
1841 * -- If ERROR_INVALID_LIST_FORMAT is returned, the format of an
1842 * export entry wasn't understood here.
1843 *
1844 * Even if NO_ERROR is returned, the array pointer might still
1845 * be NULL if the module contains no such data.
1846 *
1847 *@@added V0.9.9 (2001-03-11) [lafaix]
1848 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1849 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1850 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
1851 */
1852
1853APIRET doshExecQueryExportedFunctions(PEXECUTABLE pExec,
1854 PFSYSFUNCTION *ppaFunctions, // out: functions array
1855 PULONG pcFunctions) // out: array item count
1856{
1857 if ( (pExec)
1858 && ( (pExec->ulOS == EXEOS_OS2)
1859 || (pExec->ulOS == EXEOS_WIN16)
1860 || (pExec->ulOS == EXEOS_WIN386)
1861 )
1862 )
1863 {
1864 ENSURE_BEGIN;
1865 ULONG cFunctions = 0;
1866 PFSYSFUNCTION paFunctions = NULL;
1867
1868 ULONG ulDummy;
1869
1870 if (pExec->ulExeFormat == EXEFORMAT_LX)
1871 {
1872 // It's a 32bit OS/2 executable
1873
1874 // the number of exported entry points is not stored
1875 // in the executable header; we have to count them in
1876 // the entry table
1877
1878 ENSURE(ScanLXEntryTable(pExec, NULL, &cFunctions));
1879
1880 // we now have the number of exported entries; let us
1881 // build them
1882
1883 if (cFunctions)
1884 {
1885 ULONG cb = sizeof(FSYSFUNCTION) * cFunctions;
1886
1887 paFunctions = (PFSYSFUNCTION)malloc(cb);
1888 if (!paFunctions)
1889 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1890
1891 // we rescan the entry table (the cost is not as bad
1892 // as it may seem, due to disk caching)
1893
1894 ENSURE_SAFE(ScanLXEntryTable(pExec, paFunctions, NULL));
1895
1896 // we now scan the resident name table entries
1897
1898 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1899 pExec->pLXHeader->ulResdNameTblOfs
1900 + pExec->pDosExeHeader->ulNewHeaderOfs,
1901 FILE_BEGIN,
1902 &ulDummy));
1903
1904 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1905
1906 // we now scan the non-resident name table entries,
1907 // whose offset is _from the begining of the file_
1908
1909 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1910 pExec->pLXHeader->ulNonResdNameTblOfs,
1911 FILE_BEGIN,
1912 &ulDummy));
1913
1914 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1915 } // end if (cFunctions)
1916 }
1917 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1918 {
1919 // it's a "new" segmented 16bit executable
1920
1921 // here too the number of exported entry points
1922 // is not stored in the executable header; we
1923 // have to count them in the entry table
1924
1925 ENSURE(ScanNEEntryTable(pExec, NULL, &cFunctions));
1926
1927 // we now have the number of exported entries; let us
1928 // build them
1929
1930 if (cFunctions)
1931 {
1932 USHORT usOrdinal = 1,
1933 usCurrent = 0;
1934
1935 paFunctions = (PFSYSFUNCTION)malloc(sizeof(FSYSFUNCTION) * cFunctions);
1936 if (!paFunctions)
1937 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1938
1939 // we rescan the entry table (the cost is not as bad
1940 // as it may seem, due to disk caching)
1941
1942 ENSURE_SAFE(ScanNEEntryTable(pExec, paFunctions, NULL));
1943
1944 // we now scan the resident name table entries
1945
1946 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1947 pExec->pNEHeader->usResdNameTblOfs
1948 + pExec->pDosExeHeader->ulNewHeaderOfs,
1949 FILE_BEGIN,
1950 &ulDummy));
1951
1952 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1953
1954 // we now scan the non-resident name table entries,
1955 // whose offset is _from the begining of the file_
1956
1957 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
1958 pExec->pNEHeader->ulNonResdTblOfs,
1959 FILE_BEGIN,
1960 &ulDummy));
1961
1962 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1963 }
1964 }
1965 else
1966 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1967
1968 // no error: output data
1969 *ppaFunctions = paFunctions;
1970 *pcFunctions = cFunctions;
1971
1972 ENSURE_FINALLY;
1973 // if we had an error above, clean up
1974 free(paFunctions);
1975 ENSURE_END;
1976 }
1977 else
1978 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1979
1980 ENSURE_OK;
1981}
1982
1983/*
1984 *@@ doshExecFreeExportedFunctions:
1985 * frees resources allocated by doshExecQueryExportedFunctions.
1986 *
1987 *@@added V0.9.9 (2001-03-11)
1988 */
1989
1990APIRET doshExecFreeExportedFunctions(PFSYSFUNCTION paFunctions)
1991{
1992 free(paFunctions);
1993
1994 return (NO_ERROR);
1995}
1996
1997/*
1998 *@@ doshExecQueryResources:
1999 * returns an array of FSYSRESOURCE structures describing all
2000 * available resources in the module.
2001 *
2002 * *pcResources receives the no. of items in the array
2003 * (not the array size!). Use doshExecFreeResources to clean up.
2004 *
2005 * This returns a standard OS/2 error code, which might be
2006 * any of the codes returned by DosSetFilePtr and DosRead.
2007 * In addition, this may return:
2008 *
2009 * -- ERROR_NOT_ENOUGH_MEMORY
2010 *
2011 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
2012 * than LX or NE, which is not understood by this function.
2013 *
2014 * Even if NO_ERROR is returned, the array pointer might still
2015 * be NULL if the module contains no such data.
2016 *
2017 *@@added V0.9.7 (2000-12-18) [lafaix]
2018 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
2019 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
2020 */
2021
2022APIRET doshExecQueryResources(PEXECUTABLE pExec, // in: executable from doshExecOpen
2023 PFSYSRESOURCE *ppaResources, // out: res's array
2024 PULONG pcResources) // out: array item count
2025{
2026 if ( (pExec)
2027 && ( (pExec->ulOS == EXEOS_OS2)
2028 || (pExec->ulOS == EXEOS_WIN16)
2029 || (pExec->ulOS == EXEOS_WIN386)
2030 )
2031 )
2032 {
2033 ENSURE_BEGIN;
2034 ULONG cResources = 0;
2035 PFSYSRESOURCE paResources = NULL;
2036
2037 if (pExec->ulExeFormat == EXEFORMAT_LX)
2038 {
2039 // 32-bit OS/2 executable:
2040 cResources = pExec->pLXHeader->ulResTblCnt;
2041
2042 if (cResources)
2043 {
2044 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2045 struct rsrc32 /* Resource Table Entry */
2046 {
2047 unsigned short type; /* Resource type */
2048 unsigned short name; /* Resource name */
2049 unsigned long cb; /* Resource size */
2050 unsigned short obj; /* Object number */
2051 unsigned long offset; /* Offset within object */
2052 } rs;
2053
2054 struct o32_obj /* Flat .EXE object table entry */
2055 {
2056 unsigned long o32_size; /* Object virtual size */
2057 unsigned long o32_base; /* Object base virtual address */
2058 unsigned long o32_flags; /* Attribute flags */
2059 unsigned long o32_pagemap; /* Object page map index */
2060 unsigned long o32_mapsize; /* Number of entries in object page map */
2061 unsigned long o32_reserved; /* Reserved */
2062 } ot;
2063 #pragma pack() // V0.9.9 (2001-04-03) [umoeller]
2064
2065 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2066 ULONG ulDummy;
2067 int i;
2068
2069 paResources = (PFSYSRESOURCE)malloc(cb);
2070 if (!paResources)
2071 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2072
2073 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2074
2075 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2076 pExec->pLXHeader->ulResTblOfs
2077 + pExec->pDosExeHeader->ulNewHeaderOfs,
2078 FILE_BEGIN,
2079 &ulDummy));
2080
2081 for (i = 0; i < cResources; i++)
2082 {
2083 ENSURE_SAFE(DosRead(pExec->hfExe, &rs, 14, &ulDummy));
2084
2085 paResources[i].ulID = rs.name;
2086 paResources[i].ulType = rs.type;
2087 paResources[i].ulSize = rs.cb;
2088 paResources[i].ulFlag = rs.obj; // Temp storage for Object
2089 // number. Will be filled
2090 // with resource flag
2091 // later.
2092 }
2093
2094 for (i = 0; i < cResources; i++)
2095 {
2096 ULONG ulOfsThis = pExec->pLXHeader->ulObjTblOfs
2097 + pExec->pDosExeHeader->ulNewHeaderOfs
2098 + ( sizeof(ot)
2099 * (paResources[i].ulFlag - 1));
2100
2101 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2102 ulOfsThis,
2103 FILE_BEGIN,
2104 &ulDummy));
2105
2106 ENSURE_SAFE(DosRead(pExec->hfExe, &ot, sizeof(ot), &ulDummy));
2107
2108 paResources[i].ulFlag = ((ot.o32_flags & OBJWRITE)
2109 ? 0
2110 : RNPURE);
2111 paResources[i].ulFlag |= ((ot.o32_flags & OBJDISCARD)
2112 ? 4096
2113 : 0);
2114 paResources[i].ulFlag |= ((ot.o32_flags & OBJSHARED)
2115 ? RNMOVE
2116 : 0);
2117 paResources[i].ulFlag |= ((ot.o32_flags & OBJPRELOAD)
2118 ? RNPRELOAD
2119 : 0);
2120 } // end for
2121 } // end if (cResources)
2122 } // end if (pExec->ulExeFormat == EXEFORMAT_LX)
2123 else if (pExec->ulExeFormat == EXEFORMAT_NE)
2124 {
2125 if (pExec->ulOS == EXEOS_OS2)
2126 {
2127 // 16-bit OS/2 executable:
2128 cResources = pExec->pNEHeader->usResSegmCount;
2129
2130 if (cResources)
2131 {
2132 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
2133 struct {unsigned short type; unsigned short name;} rti;
2134 struct new_seg /* New .EXE segment table entry */
2135 {
2136 unsigned short ns_sector; /* File sector of start of segment */
2137 unsigned short ns_cbseg; /* Number of bytes in file */
2138 unsigned short ns_flags; /* Attribute flags */
2139 unsigned short ns_minalloc; /* Minimum allocation in bytes */
2140 } ns;
2141 #pragma pack()
2142
2143 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2144 ULONG ulDummy;
2145 int i;
2146
2147 paResources = (PFSYSRESOURCE)malloc(cb);
2148 if (!paResources)
2149 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2150
2151 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
2152
2153 // we first read the resources IDs and types
2154
2155 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2156 pExec->pNEHeader->usResTblOfs
2157 + pExec->pDosExeHeader->ulNewHeaderOfs,
2158 FILE_BEGIN,
2159 &ulDummy));
2160
2161 for (i = 0; i < cResources; i++)
2162 {
2163 ENSURE_SAFE(DosRead(pExec->hfExe, &rti, sizeof(rti), &ulDummy));
2164
2165 paResources[i].ulID = rti.name;
2166 paResources[i].ulType = rti.type;
2167 }
2168
2169 // we then read their sizes and flags
2170
2171 for (i = 0; i < cResources; i++)
2172 {
2173 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2174 pExec->pDosExeHeader->ulNewHeaderOfs
2175 + pExec->pNEHeader->usSegTblOfs
2176 + (sizeof(ns)
2177 * ( pExec->pNEHeader->usSegTblEntries
2178 - pExec->pNEHeader->usResSegmCount
2179 + i)),
2180 FILE_BEGIN,
2181 &ulDummy));
2182
2183 ENSURE_SAFE(DosRead(pExec->hfExe, &ns, sizeof(ns), &ulDummy));
2184
2185 paResources[i].ulSize = ns.ns_cbseg;
2186
2187 paResources[i].ulFlag = (ns.ns_flags & OBJPRELOAD) ? RNPRELOAD : 0;
2188 paResources[i].ulFlag |= (ns.ns_flags & OBJSHARED) ? RNPURE : 0;
2189 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? RNMOVE : 0;
2190 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? 4096 : 0;
2191 }
2192 } // end if (cResources)
2193 }
2194 else
2195 {
2196 // 16-bit Windows executable
2197 USHORT usAlignShift;
2198 ULONG ulDummy;
2199
2200 ENSURE(DosSetFilePtr(pExec->hfExe,
2201 pExec->pNEHeader->usResTblOfs
2202 + pExec->pDosExeHeader->ulNewHeaderOfs,
2203 FILE_BEGIN,
2204 &ulDummy));
2205
2206 ENSURE(DosRead(pExec->hfExe,
2207 &usAlignShift,
2208 sizeof(usAlignShift),
2209 &ulDummy));
2210
2211 while (TRUE)
2212 {
2213 USHORT usTypeID;
2214 USHORT usCount;
2215
2216 ENSURE(DosRead(pExec->hfExe,
2217 &usTypeID,
2218 sizeof(usTypeID),
2219 &ulDummy));
2220
2221 if (usTypeID == 0)
2222 break;
2223
2224 ENSURE(DosRead(pExec->hfExe,
2225 &usCount,
2226 sizeof(usCount),
2227 &ulDummy));
2228
2229 ENSURE(DosSetFilePtr(pExec->hfExe,
2230 sizeof(ULONG),
2231 FILE_CURRENT,
2232 &ulDummy));
2233
2234 cResources += usCount;
2235
2236 // first pass, skip NAMEINFO table
2237 ENSURE(DosSetFilePtr(pExec->hfExe,
2238 usCount*6*sizeof(USHORT),
2239 FILE_CURRENT,
2240 &ulDummy));
2241 }
2242
2243 if (cResources)
2244 {
2245 USHORT usCurrent = 0;
2246 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
2247
2248 paResources = (PFSYSRESOURCE)malloc(cb);
2249 if (!paResources)
2250 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
2251
2252 memset(paResources, 0, cb);
2253
2254 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2255 pExec->pNEHeader->usResTblOfs
2256 + pExec->pDosExeHeader->ulNewHeaderOfs,
2257 FILE_BEGIN,
2258 &ulDummy));
2259
2260 ENSURE_SAFE(DosRead(pExec->hfExe,
2261 &usAlignShift,
2262 sizeof(usAlignShift),
2263 &ulDummy));
2264
2265 while (TRUE)
2266 {
2267 USHORT usTypeID;
2268 USHORT usCount;
2269 int i;
2270
2271 ENSURE_SAFE(DosRead(pExec->hfExe,
2272 &usTypeID,
2273 sizeof(usTypeID),
2274 &ulDummy));
2275
2276 if (usTypeID == 0)
2277 break;
2278
2279 ENSURE_SAFE(DosRead(pExec->hfExe,
2280 &usCount,
2281 sizeof(usCount),
2282 &ulDummy));
2283
2284 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2285 sizeof(ULONG),
2286 FILE_CURRENT,
2287 &ulDummy));
2288
2289 // second pass, read NAMEINFO table
2290 for (i = 0; i < usCount; i++)
2291 {
2292 USHORT usLength,
2293 usFlags,
2294 usID;
2295
2296 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2297 sizeof(USHORT),
2298 FILE_CURRENT,
2299 &ulDummy));
2300
2301 ENSURE_SAFE(DosRead(pExec->hfExe,
2302 &usLength,
2303 sizeof(USHORT),
2304 &ulDummy));
2305 ENSURE_SAFE(DosRead(pExec->hfExe,
2306 &usFlags,
2307 sizeof(USHORT),
2308 &ulDummy));
2309 ENSURE_SAFE(DosRead(pExec->hfExe,
2310 &usID,
2311 sizeof(USHORT),
2312 &ulDummy));
2313
2314 ENSURE_SAFE(DosSetFilePtr(pExec->hfExe,
2315 2*sizeof(USHORT),
2316 FILE_CURRENT,
2317 &ulDummy));
2318
2319 // !!! strings ids and types not handled yet
2320 // !!! 15th bit is used to denotes strings
2321 // !!! offsets [lafaix]
2322 paResources[usCurrent].ulType = usTypeID ^ 0x8000;
2323 paResources[usCurrent].ulID = usID ^ 0x8000;
2324 paResources[usCurrent].ulSize = usLength << usAlignShift;
2325 paResources[usCurrent].ulFlag = usFlags & 0x70;
2326
2327 usCurrent++;
2328 }
2329 }
2330 }
2331 }
2332 } // end else if (pExec->ulExeFormat == EXEFORMAT_NE)
2333 else
2334 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2335
2336 *ppaResources = paResources;
2337 *pcResources = cResources;
2338
2339 ENSURE_FINALLY;
2340 // if we had an error above, clean up
2341 free(paResources);
2342 ENSURE_END;
2343 }
2344 else
2345 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
2346
2347 ENSURE_OK;
2348}
2349
2350/*
2351 *@@ doshExecFreeResources:
2352 * frees resources allocated by doshExecQueryResources.
2353 *
2354 *@@added V0.9.7 (2000-12-18) [lafaix]
2355 */
2356
2357APIRET doshExecFreeResources(PFSYSRESOURCE paResources)
2358{
2359 free(paResources);
2360 return (NO_ERROR);
2361}
2362
2363/*
2364 * FindFile:
2365 * helper for doshFindExecutable.
2366 *
2367 *added V0.9.11 (2001-04-25) [umoeller]
2368 */
2369
2370APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
2371 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
2372 ULONG cbExecutable) // in: sizeof (*pszExecutable)
2373{
2374 APIRET arc = NO_ERROR;
2375 FILESTATUS3 fs3;
2376
2377 if ( (strchr(pcszCommand, '\\'))
2378 || (strchr(pcszCommand, ':'))
2379 )
2380 {
2381 // looks like this is qualified:
2382 arc = DosQueryPathInfo((PSZ)pcszCommand,
2383 FIL_STANDARD,
2384 &fs3,
2385 sizeof(fs3));
2386 if (!arc)
2387 if (!(fs3.attrFile & FILE_DIRECTORY))
2388 strhncpy0(pszExecutable,
2389 pcszCommand,
2390 cbExecutable);
2391 else
2392 // directory:
2393 arc = ERROR_INVALID_EXE_SIGNATURE;
2394 }
2395 else
2396 // non-qualified:
2397 arc = DosSearchPath(SEARCH_IGNORENETERRS | SEARCH_ENVIRONMENT | SEARCH_CUR_DIRECTORY,
2398 "PATH",
2399 (PSZ)pcszCommand,
2400 pszExecutable,
2401 cbExecutable);
2402
2403 return (arc);
2404}
2405
2406/*
2407 *@@ doshFindExecutable:
2408 * this attempts to find an executable by doing the
2409 * following:
2410 *
2411 * 1) If pcszCommand appears to be qualified (i.e. contains
2412 * a backslash), this checks for whether the file exists.
2413 *
2414 * 2) If pcszCommand contains no backslash, this calls
2415 * DosSearchPath in order to find the full path of the
2416 * executable.
2417 *
2418 * papcszExtensions determines if additional searches are to be
2419 * performed if the file doesn't exist (case 1) or DosSearchPath
2420 * returned ERROR_FILE_NOT_FOUND (case 2).
2421 * This must point to an array of strings specifying the extra
2422 * extensions to search for.
2423 *
2424 * If both papcszExtensions and cExtensions are null, no
2425 * extra searches are performed.
2426 *
2427 * If this returns NO_ERROR, pszExecutable receives
2428 * the full path of the executable found by DosSearchPath.
2429 * Otherwise ERROR_FILE_NOT_FOUND is returned.
2430 *
2431 * Example:
2432 *
2433 + const char *aExtensions[] = { "EXE",
2434 + "COM",
2435 + "CMD"
2436 + };
2437 + CHAR szExecutable[CCHMAXPATH];
2438 + APIRET arc = doshFindExecutable("lvm",
2439 + szExecutable,
2440 + sizeof(szExecutable),
2441 + aExtensions,
2442 + 3);
2443 *
2444 *@@added V0.9.9 (2001-03-07) [umoeller]
2445 *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
2446 */
2447
2448APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
2449 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
2450 ULONG cbExecutable, // in: sizeof (*pszExecutable)
2451 const char **papcszExtensions, // in: array of extensions (without dots)
2452 ULONG cExtensions) // in: array item count
2453{
2454 APIRET arc = FindFile(pcszCommand,
2455 pszExecutable,
2456 cbExecutable);
2457
2458 if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
2459 && (cExtensions) // any extra searches wanted?
2460 )
2461 {
2462 // try additional things then
2463 PSZ psz2 = (PSZ)malloc(strlen(pcszCommand) + 20);
2464 if (psz2)
2465 {
2466 ULONG ul;
2467 for (ul = 0;
2468 ul < cExtensions;
2469 ul++)
2470 {
2471 const char *pcszExtThis = papcszExtensions[ul];
2472 sprintf(psz2, "%s.%s", pcszCommand, pcszExtThis);
2473 arc = FindFile(psz2,
2474 pszExecutable,
2475 cbExecutable);
2476 if (arc != ERROR_FILE_NOT_FOUND)
2477 break;
2478 }
2479
2480 free(psz2);
2481 }
2482 else
2483 arc = ERROR_NOT_ENOUGH_MEMORY;
2484 }
2485
2486 return (arc);
2487}
2488
2489/*
2490 *@@category: Helpers\Control program helpers\Partitions info
2491 * functions for retrieving partition information directly
2492 * from the partition tables on the disk. See doshGetPartitionsList.
2493 */
2494
2495/********************************************************************
2496 *
2497 * Partition functions
2498 *
2499 ********************************************************************/
2500
2501/*
2502 *@@ doshQueryDiskCount:
2503 * returns the no. of physical disks installed
2504 * on the system.
2505 *
2506 * Based on code (C) Dmitry A. Steklenev.
2507 *
2508 *@@added V0.9.0 [umoeller]
2509 */
2510
2511UINT doshQueryDiskCount(VOID)
2512{
2513 USHORT count = 0;
2514
2515 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &count, 2, 0, 0);
2516 return (count);
2517}
2518
2519/*
2520 *@@ doshReadSector:
2521 * reads a physical disk sector.
2522 *
2523 * If NO_ERROR is returned, the sector contents
2524 * have been stored in *buff.
2525 *
2526 * Based on code (C) Dmitry A. Steklenev.
2527 *
2528 *@@added V0.9.0 [umoeller]
2529 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
2530 */
2531
2532APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
2533 void *buff,
2534 USHORT head,
2535 USHORT cylinder,
2536 USHORT sector)
2537{
2538 APIRET arc;
2539 HFILE dh = 0;
2540 char dn[256];
2541
2542 sprintf(dn, "%u:", disk);
2543 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
2544 {
2545 TRACKLAYOUT DiskIOParm;
2546 ULONG IOCtlDataLength = sizeof(DiskIOParm);
2547 ULONG IOCtlParmLength = 512;
2548
2549 DiskIOParm.bCommand = 0;
2550 DiskIOParm.usHead = head;
2551 DiskIOParm.usCylinder = cylinder;
2552 DiskIOParm.usFirstSector = 0;
2553 DiskIOParm.cSectors = 1;
2554 DiskIOParm.TrackTable[0].usSectorNumber = sector;
2555 DiskIOParm.TrackTable[0].usSectorSize = 512;
2556
2557 arc = DosDevIOCtl(dh,
2558 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
2559 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
2560 buff , IOCtlDataLength, &IOCtlDataLength);
2561
2562 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
2563 }
2564
2565 return (arc);
2566}
2567
2568/*
2569 *@@ doshType2FSName:
2570 * this returns a static, zero-terminated string
2571 * for the given FS type. This is always 7 bytes
2572 * in length.
2573 *
2574 * Values for operating system indicator:
2575 * -- 00h empty
2576 * -- 01h DOS 12-bit FAT
2577 * -- 02h XENIX root file system
2578 * -- 03h XENIX /usr file system (obsolete)
2579 * -- 04h DOS 16-bit FAT (up to 32M)
2580 * -- 05h DOS 3.3+ extended partition
2581 * -- 06h DOS 3.31+ Large File System (16-bit FAT, over 32M)
2582 * -- 07h QNX
2583 * -- 07h OS/2 HPFS
2584 * -- 07h Windows NT NTFS
2585 * -- 07h Advanced Unix
2586 * -- 08h OS/2 (v1.0-1.3 only)
2587 * -- 08h AIX bootable partition, SplitDrive
2588 * -- 08h Commodore DOS
2589 * -- 08h DELL partition spanning multiple drives
2590 * -- 09h AIX data partition
2591 * -- 09h Coherent filesystem
2592 * -- 0Ah OS/2 Boot Manager
2593 * -- 0Ah OPUS
2594 * -- 0Ah Coherent swap partition
2595 * -- 0Bh Windows95 with 32-bit FAT
2596 * -- 0Ch Windows95 with 32-bit FAT (using LBA-mode INT 13 extensions)
2597 * -- 0Eh logical-block-addressable VFAT (same as 06h but using LBA-mode INT 13)
2598 * -- 0Fh logical-block-addressable VFAT (same as 05h but using LBA-mode INT 13)
2599 * -- 10h OPUS
2600 * -- 11h OS/2 Boot Manager hidden 12-bit FAT partition
2601 * -- 12h Compaq Diagnostics partition
2602 * -- 14h (resulted from using Novell DOS 7.0 FDISK to delete Linux Native part)
2603 * -- 14h OS/2 Boot Manager hidden sub-32M 16-bit FAT partition
2604 * -- 16h OS/2 Boot Manager hidden over-32M 16-bit FAT partition
2605 * -- 17h OS/2 Boot Manager hidden HPFS partition
2606 * -- 18h AST special Windows swap file ("Zero-Volt Suspend" partition)
2607 * -- 21h officially listed as reserved
2608 * -- 23h officially listed as reserved
2609 * -- 24h NEC MS-DOS 3.x
2610 * -- 26h officially listed as reserved
2611 * -- 31h officially listed as reserved
2612 * -- 33h officially listed as reserved
2613 * -- 34h officially listed as reserved
2614 * -- 36h officially listed as reserved
2615 * -- 38h Theos
2616 * -- 3Ch PowerQuest PartitionMagic recovery partition
2617 * -- 40h VENIX 80286
2618 * -- 41h Personal RISC Boot
2619 * -- 42h SFS (Secure File System) by Peter Gutmann
2620 * -- 50h OnTrack Disk Manager, read-only partition
2621 * -- 51h OnTrack Disk Manager, read/write partition
2622 * -- 51h NOVEL
2623 * -- 52h CP/M
2624 * -- 52h Microport System V/386
2625 * -- 53h OnTrack Disk Manager, write-only partition???
2626 * -- 54h OnTrack Disk Manager (DDO)
2627 * -- 56h GoldenBow VFeature
2628 * -- 61h SpeedStor
2629 * -- 63h Unix SysV/386, 386/ix
2630 * -- 63h Mach, MtXinu BSD 4.3 on Mach
2631 * -- 63h GNU HURD
2632 * -- 64h Novell NetWare 286
2633 * -- 65h Novell NetWare (3.11)
2634 * -- 67h Novell
2635 * -- 68h Novell
2636 * -- 69h Novell
2637 * -- 70h DiskSecure Multi-Boot
2638 * -- 71h officially listed as reserved
2639 * -- 73h officially listed as reserved
2640 * -- 74h officially listed as reserved
2641 * -- 75h PC/IX
2642 * -- 76h officially listed as reserved
2643 * -- 80h Minix v1.1 - 1.4a
2644 * -- 81h Minix v1.4b+
2645 * -- 81h Linux
2646 * -- 81h Mitac Advanced Disk Manager
2647 * -- 82h Linux Swap partition
2648 * -- 82h Prime
2649 * -- 83h Linux native file system (ext2fs/xiafs)
2650 * -- 84h OS/2-renumbered type 04h partition (related to hiding DOS C: drive)
2651 * -- 86h FAT16 volume/stripe set (Windows NT)
2652 * -- 87h HPFS Fault-Tolerant mirrored partition
2653 * -- 87h NTFS volume/stripe set
2654 * -- 93h Amoeba file system
2655 * -- 94h Amoeba bad block table
2656 * -- A0h Phoenix NoteBIOS Power Management "Save-to-Disk" partition
2657 * -- A1h officially listed as reserved
2658 * -- A3h officially listed as reserved
2659 * -- A4h officially listed as reserved
2660 * -- A5h FreeBSD, BSD/386
2661 * -- A6h officially listed as reserved
2662 * -- B1h officially listed as reserved
2663 * -- B3h officially listed as reserved
2664 * -- B4h officially listed as reserved
2665 * -- B6h officially listed as reserved
2666 * -- B7h BSDI file system (secondarily swap)
2667 * -- B8h BSDI swap partition (secondarily file system)
2668 * -- C1h DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT partition
2669 * -- C4h DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT partition
2670 * -- C6h DR DOS 6.0 LOGIN.EXE-secured Huge partition
2671 * -- C6h corrupted FAT16 volume/stripe set (Windows NT)
2672 * -- C7h Syrinx Boot
2673 * -- C7h corrupted NTFS volume/stripe set
2674 * -- D8h CP/M-86
2675 * -- DBh CP/M, Concurrent CP/M, Concurrent DOS
2676 * -- DBh CTOS (Convergent Technologies OS)
2677 * -- E1h SpeedStor 12-bit FAT extended partition
2678 * -- E3h DOS read-only
2679 * -- E3h Storage Dimensions
2680 * -- E4h SpeedStor 16-bit FAT extended partition
2681 * -- E5h officially listed as reserved
2682 * -- E6h officially listed as reserved
2683 * -- F1h Storage Dimensions
2684 * -- F2h DOS 3.3+ secondary partition
2685 * -- F3h officially listed as reserved
2686 * -- F4h SpeedStor
2687 * -- F4h Storage Dimensions
2688 * -- F6h officially listed as reserved
2689 * -- FEh LANstep
2690 * -- FEh IBM PS/2 IML
2691 * -- FFh Xenix bad block table
2692 *
2693 * Note: for partition type 07h, one should inspect the partition boot record
2694 * for the actual file system type
2695 *
2696 * Based on code (C) Dmitry A. Steklenev.
2697 *
2698 *@@added V0.9.0 [umoeller]
2699 */
2700
2701const char* doshType2FSName(unsigned char bFSType) // in: FS type
2702{
2703 PSZ zFSName = NULL;
2704
2705 switch (bFSType)
2706 {
2707 case PAR_UNUSED:
2708 zFSName = "UNUSED ";
2709 break;
2710 case PAR_FAT12SMALL:
2711 zFSName = "FAT-12 ";
2712 break;
2713 case PAR_XENIXROOT:
2714 zFSName = "XENIX ";
2715 break;
2716 case PAR_XENIXUSER:
2717 zFSName = "XENIX ";
2718 break;
2719 case PAR_FAT16SMALL:
2720 zFSName = "FAT-16 ";
2721 break;
2722 case PAR_EXTENDED:
2723 zFSName = "EXTEND ";
2724 break;
2725 case PAR_FAT16BIG:
2726 zFSName = "BIGDOS ";
2727 break;
2728 case PAR_HPFS:
2729 zFSName = "HPFS ";
2730 break;
2731 case PAR_AIXBOOT:
2732 zFSName = "AIX ";
2733 break;
2734 case PAR_AIXDATA:
2735 zFSName = "AIX ";
2736 break;
2737 case PAR_BOOTMANAGER:
2738 zFSName = "BOOTMNG";
2739 break;
2740 case PAR_WINDOWS95:
2741 zFSName = "WIN95 ";
2742 break;
2743 case PAR_WINDOWS95LB:
2744 zFSName = "WIN95 ";
2745 break;
2746 case PAR_VFAT16BIG:
2747 zFSName = "VFAT ";
2748 break;
2749 case PAR_VFAT16EXT:
2750 zFSName = "VFAT ";
2751 break;
2752 case PAR_OPUS:
2753 zFSName = "OPUS ";
2754 break;
2755 case PAR_HID12SMALL:
2756 zFSName = "FAT-12*";
2757 break;
2758 case PAR_COMPAQDIAG:
2759 zFSName = "COMPAQ ";
2760 break;
2761 case PAR_HID16SMALL:
2762 zFSName = "FAT-16*";
2763 break;
2764 case PAR_HID16BIG:
2765 zFSName = "BIGDOS*";
2766 break;
2767 case PAR_HIDHPFS:
2768 zFSName = "HPFS* ";
2769 break;
2770 case PAR_WINDOWSSWP:
2771 zFSName = "WINSWAP";
2772 break;
2773 case PAR_NECDOS:
2774 zFSName = "NECDOS ";
2775 break;
2776 case PAR_THEOS:
2777 zFSName = "THEOS ";
2778 break;
2779 case PAR_VENIX:
2780 zFSName = "VENIX ";
2781 break;
2782 case PAR_RISCBOOT:
2783 zFSName = "RISC ";
2784 break;
2785 case PAR_SFS:
2786 zFSName = "SFS ";
2787 break;
2788 case PAR_ONTRACK:
2789 zFSName = "ONTRACK";
2790 break;
2791 case PAR_ONTRACKEXT:
2792 zFSName = "ONTRACK";
2793 break;
2794 case PAR_CPM:
2795 zFSName = "CP/M ";
2796 break;
2797 case PAR_UNIXSYSV:
2798 zFSName = "UNIX ";
2799 break;
2800 case PAR_NOVELL_64:
2801 zFSName = "NOVELL ";
2802 break;
2803 case PAR_NOVELL_65:
2804 zFSName = "NOVELL ";
2805 break;
2806 case PAR_NOVELL_67:
2807 zFSName = "NOVELL ";
2808 break;
2809 case PAR_NOVELL_68:
2810 zFSName = "NOVELL ";
2811 break;
2812 case PAR_NOVELL_69:
2813 zFSName = "NOVELL ";
2814 break;
2815 case PAR_PCIX:
2816 zFSName = "PCIX ";
2817 break;
2818 case PAR_MINIX:
2819 zFSName = "MINIX ";
2820 break;
2821 case PAR_LINUX:
2822 zFSName = "LINUX ";
2823 break;
2824 case PAR_LINUXSWAP:
2825 zFSName = "LNXSWP ";
2826 break;
2827 case PAR_LINUXFILE:
2828 zFSName = "LINUX ";
2829 break;
2830 case PAR_FREEBSD:
2831 zFSName = "FREEBSD";
2832 break;
2833 case PAR_BBT:
2834 zFSName = "BBT ";
2835 break;
2836
2837 default:
2838 zFSName = " ";
2839 break;
2840 }
2841 return zFSName;
2842}
2843
2844/*
2845 * AppendPartition:
2846 * this appends the given partition information to
2847 * the given partition list. To do this, a new
2848 * PARTITIONINFO structure is created and appended
2849 * in a list (managed thru the PARTITIONINFO.pNext
2850 * items).
2851 *
2852 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
2853 * With each call of this function, this pointer is advanced
2854 * to point to the newly created PARTITIONINFO, so before
2855 * calling this function for the first time,
2856 *
2857 * Based on code (C) Dmitry A. Steklenev.
2858 *
2859 *@@added V0.9.0 [umoeller]
2860 */
2861
2862APIRET AppendPartition(PARTITIONINFO **pppiFirst,
2863 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
2864 PUSHORT posCount, // in/out: partition count
2865 BYTE bDisk, // in: disk of partition
2866 const char *pszBootName, // in: boot partition name
2867 CHAR cLetter, // in/out: drive letter
2868 BYTE bFsType, // in: file system type
2869 BOOL fPrimary, // in: primary?
2870 BOOL fBootable,
2871 ULONG ulSectors) // in: no. of sectors
2872{
2873 APIRET arc = NO_ERROR;
2874 PPARTITIONINFO ppiNew = NEW(PARTITIONINFO);
2875 if (ppiNew)
2876 {
2877 ZERO(ppiNew);
2878
2879 // store data
2880 ppiNew->bDisk = bDisk;
2881 if ((fBootable) && (pszBootName) )
2882 {
2883 memcpy(ppiNew->szBootName, pszBootName, 8);
2884 ppiNew->szBootName[8] = 0;
2885 }
2886 else
2887 ppiNew->szBootName[0] = 0;
2888 ppiNew->cLetter = cLetter;
2889 ppiNew->bFSType = bFsType;
2890 strcpy(ppiNew->szFSType,
2891 doshType2FSName(bFsType));
2892 ppiNew->fPrimary = fPrimary;
2893 ppiNew->fBootable = fBootable;
2894 ppiNew->ulSize = ulSectors / 2048;
2895
2896 ppiNew->pNext = NULL;
2897
2898 (*posCount)++;
2899
2900 if (*pppiFirst == (PPARTITIONINFO)NULL)
2901 {
2902 // first call:
2903 *pppiFirst = ppiNew;
2904 *pppiThis = ppiNew;
2905 }
2906 else
2907 {
2908 // append to list
2909 (**pppiThis).pNext = ppiNew;
2910 *pppiThis = ppiNew;
2911 }
2912 }
2913 else
2914 arc = ERROR_NOT_ENOUGH_MEMORY;
2915
2916 return (arc);
2917}
2918
2919// Sector and Cylinder values are actually 6 bits and 10 bits:
2920//
2921// 1 1 1 1 1 1
2922// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
2923// ³c c c c c c c c C c S s s s s s³
2924// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
2925//
2926// The high two bits of the second byte are used as the high bits
2927// of a 10-bit value. This allows for as many as 1024 cylinders
2928// and 64 sectors per cylinder.
2929
2930/*
2931 * GetCyl:
2932 * get cylinder number.
2933 *
2934 * Based on code (C) Dmitry A. Steklenev.
2935 *
2936 *@@added V0.9.0 [umoeller]
2937 */
2938
2939static USHORT GetCyl(USHORT rBeginSecCyl)
2940{
2941 return ( (rBeginSecCyl & 0x00C0) << 2)
2942 + ((rBeginSecCyl & 0xFF00) >> 8);
2943}
2944
2945/*
2946 * GetSec:
2947 * get sector number.
2948 *
2949 * Based on code (C) Dmitry A. Steklenev.
2950 *
2951 *@@added V0.9.0 [umoeller]
2952 */
2953
2954static USHORT GetSec(USHORT rBeginSecCyl)
2955{
2956 return rBeginSecCyl & 0x003F;
2957}
2958
2959/*
2960 *@@ doshGetBootManager:
2961 * this goes thru the master boot records on all
2962 * disks to find the boot manager partition.
2963 *
2964 * Returns:
2965 *
2966 * -- NO_ERROR: boot manager found; in that case,
2967 * information about the boot manager
2968 * is written into *pusDisk, *pusPart,
2969 * *BmInfo. Any of these pointers can
2970 * be NULL if you're not interested.
2971 *
2972 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
2973 *
2974 * Based on code (C) Dmitry A. Steklenev.
2975 *
2976 *@@added V0.9.0 [umoeller]
2977 */
2978
2979APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
2980 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
2981 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
2982{
2983 APIRET arc = NO_ERROR;
2984 USHORT count = doshQueryDiskCount(); // Physical disk number
2985 MBR_INFO MBoot; // Master Boot
2986 USHORT usDisk;
2987
2988 if (count > 8) // Not above 8 disks
2989 count = 8;
2990
2991 for (usDisk = 1; usDisk <= count; usDisk++)
2992 {
2993 USHORT usPrim = 0;
2994
2995 // for each disk, read the MBR, which has the
2996 // primary partitions
2997 if ((arc = doshReadSector(usDisk,
2998 &MBoot,
2999 0, // head
3000 0, // cylinder
3001 1))) // sector
3002 return (arc);
3003
3004 // scan primary partitions for whether
3005 // BootManager partition exists
3006 for (usPrim = 0; usPrim < 4; usPrim++)
3007 {
3008 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
3009 {
3010 // this is boot manager:
3011 if (pBmInfo)
3012 *pBmInfo = MBoot.sPrtnInfo[usPrim];
3013 if (pusPart)
3014 *pusPart = usPrim;
3015 if (pusDisk)
3016 *pusDisk = usDisk;
3017 // stop scanning
3018 return (NO_ERROR);
3019 }
3020 }
3021 }
3022
3023 return (ERROR_NOT_SUPPORTED);
3024}
3025
3026/*
3027 * GetPrimaryPartitions:
3028 * this returns the primary partitions.
3029 *
3030 * This gets called from doshGetPartitionsList.
3031 *
3032 * Returns:
3033 *
3034 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
3035 *
3036 * Based on code (C) Dmitry A. Steklenev.
3037 *
3038 *@@added V0.9.0 [umoeller]
3039 */
3040
3041APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
3042 PARTITIONINFO **pppiThis,
3043 PUSHORT posCount, // in/out: partition count
3044 PCHAR pcLetter, // in/out: drive letter counter
3045 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
3046 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
3047 UINT iDisk) // in: system's physical disk count
3048{
3049 APIRET arc = NO_ERROR;
3050
3051 if (!pBmInfo)
3052 arc = ERROR_INVALID_PARAMETER;
3053 else
3054 {
3055 SYS_INFO MName[32]; // Name Space from Boot Manager
3056 memset(&MName, 0, sizeof(MName));
3057
3058 // read boot manager name table;
3059 // this is in the boot manager primary partition
3060 // at sector offset 3 (?!?)
3061 if (!(arc = doshReadSector(BmDisk,
3062 &MName,
3063 // head, cylinder, sector of bmgr primary partition:
3064 pBmInfo->bBeginHead,
3065 GetCyl(pBmInfo->rBeginSecCyl),
3066 GetSec(pBmInfo->rBeginSecCyl) + 3)))
3067 {
3068 // got bmgr name table:
3069 MBR_INFO MBoot; // Master Boot
3070 USHORT i;
3071
3072 // read master boot record of this disk
3073 if (!(arc = doshReadSector(iDisk,
3074 &MBoot,
3075 0, // head
3076 0, // cylinder
3077 1))) // sector
3078 {
3079 for (i = 0;
3080 i < 4; // there can be only four primary partitions
3081 i++)
3082 {
3083 // skip unused partition, BootManager or Extended partition
3084 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
3085 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER) // skip boot manager
3086 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_EXTENDED) // skip extended
3087 )
3088 {
3089 BOOL fBootable = ( (pBmInfo)
3090 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
3091 );
3092 // store this partition
3093 if ((arc = AppendPartition(pppiFirst,
3094 pppiThis,
3095 posCount,
3096 iDisk,
3097 (fBootable)
3098 ? (char*)&MName[(iDisk - 1) * 4 + i].name
3099 : "",
3100 *pcLetter,
3101 MBoot.sPrtnInfo[i].bFileSysCode,
3102 TRUE, // primary
3103 fBootable,
3104 MBoot.sPrtnInfo[i].lTotalSects)))
3105 return (arc);
3106 }
3107 }
3108 }
3109 }
3110 }
3111
3112 return (arc);
3113}
3114
3115/*
3116 * GetLogicalDrives:
3117 * this returns info for the logical drives
3118 * in the extended partition. This gets called
3119 * from GetExtendedPartition.
3120 *
3121 * This gets called from GetExtendedPartition.
3122 *
3123 * Based on code (C) Dmitry A. Steklenev.
3124 *
3125 *@@added V0.9.0 [umoeller]
3126 */
3127
3128APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
3129 PARTITIONINFO **pppiThis,
3130 PUSHORT posCount,
3131 PCHAR pcLetter,
3132 PAR_INFO* PrInfo, // in: MBR entry of extended partition
3133 UINT PrDisk,
3134 PAR_INFO* BmInfo)
3135{
3136 APIRET arc = NO_ERROR;
3137 EXT_INFO MBoot; // Master Boot
3138 USHORT i;
3139
3140 if ((arc = doshReadSector(PrDisk,
3141 &MBoot,
3142 PrInfo->bBeginHead,
3143 GetCyl(PrInfo->rBeginSecCyl),
3144 GetSec(PrInfo->rBeginSecCyl))))
3145 return (arc);
3146
3147 for (i = 0; i < 4; i++)
3148 {
3149 // skip unused partition or BootManager partition
3150 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
3151 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER)
3152 )
3153 {
3154 BOOL fBootable = FALSE;
3155 BOOL fAssignLetter = FALSE;
3156
3157 // special work around extended partition
3158 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
3159 {
3160 if ((arc = GetLogicalDrives(pppiFirst,
3161 pppiThis,
3162 posCount,
3163 pcLetter,
3164 &MBoot.sPrtnInfo[i],
3165 PrDisk,
3166 BmInfo)))
3167 return (arc);
3168
3169 continue;
3170 }
3171
3172 // raise driver letter if OS/2 would recognize this drive
3173 if ( (MBoot.sPrtnInfo[i].bFileSysCode < PAR_PCIX)
3174 )
3175 fAssignLetter = TRUE;
3176
3177 if (fAssignLetter)
3178 (*pcLetter)++;
3179
3180 fBootable = ( (BmInfo)
3181 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
3182 );
3183
3184 if ((arc = AppendPartition(pppiFirst,
3185 pppiThis,
3186 posCount,
3187 PrDisk,
3188 (fBootable)
3189 ? (char*)&MBoot.sBmNames[i].name
3190 : "",
3191 (fAssignLetter)
3192 ? *pcLetter
3193 : ' ',
3194 MBoot.sPrtnInfo[i].bFileSysCode,
3195 FALSE, // primary
3196 fBootable, // bootable
3197 MBoot.sPrtnInfo[i].lTotalSects)))
3198 return (arc);
3199 }
3200 }
3201
3202 return (NO_ERROR);
3203}
3204
3205/*
3206 * GetExtendedPartition:
3207 * this finds the extended partition on the given
3208 * drive and calls GetLogicalDrives in turn.
3209 *
3210 * This gets called from doshGetPartitionsList.
3211 *
3212 * Based on code (C) Dmitry A. Steklenev.
3213 *
3214 *@@added V0.9.0 [umoeller]
3215 */
3216
3217APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
3218 PARTITIONINFO **pppiThis,
3219 PUSHORT posCount,
3220 PCHAR pcLetter,
3221 PAR_INFO* BmInfo,
3222 UINT iDisk) // in: disk to query
3223{
3224 APIRET arc = NO_ERROR;
3225 MBR_INFO MBoot; // Master Boot
3226 USHORT i;
3227
3228 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
3229 return (arc);
3230
3231 // go thru MBR entries to find extended partition
3232 for (i = 0;
3233 i < 4;
3234 i++)
3235 {
3236 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
3237 {
3238 if ((arc = GetLogicalDrives(pppiFirst,
3239 pppiThis,
3240 posCount,
3241 pcLetter,
3242 &MBoot.sPrtnInfo[i],
3243 iDisk,
3244 BmInfo)))
3245 return (arc);
3246 }
3247 }
3248
3249 return (NO_ERROR);
3250}
3251
3252/*
3253 *@@ CleanPartitionInfos:
3254 *
3255 *@@added V0.9.9 (2001-04-07) [umoeller]
3256 */
3257
3258VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
3259{
3260 while (ppiThis)
3261 {
3262 PPARTITIONINFO ppiNext = ppiThis->pNext;
3263 free(ppiThis);
3264 ppiThis = ppiNext;
3265 }
3266}
3267
3268/*
3269 *@@ doshGetPartitionsList:
3270 * this returns lots of information about the
3271 * partitions on all physical disks, which is
3272 * read directly from the MBRs and partition
3273 * tables.
3274 *
3275 * If NO_ERROR is returned by this function,
3276 * *ppPartitionInfo points to a linked list of
3277 * PARTITIONINFO structures, which has
3278 * *pusPartitionCount items.
3279 *
3280 * In that case, use doshFreePartitionsList to
3281 * free the resources allocated by this function.
3282 *
3283 * What this function returns depends on whether
3284 * LVM is installed.
3285 *
3286 * -- If LVM.DLL is found on the LIBPATH, this opens
3287 * the LVM engine and returns the info from the
3288 * LVM engine in the PARTITIONINFO structures.
3289 * The partitions are then sorted by disk in
3290 * ascending order.
3291 *
3292 * -- Otherwise, we parse the partition tables
3293 * manually. The linked list then starts out with
3294 * all the primary partitions, followed by the
3295 * logical drives in the extended partitions.
3296 * This function attempts to guess the correct drive
3297 * letters and stores these with the PARTITIONINFO
3298 * items, but there's no guarantee that this is
3299 * correct. We correctly ignore Linux partitions here
3300 * and give all primary partitions the C: letter, but
3301 * I have no idea what happens with NTFS partitions,
3302 * since I have none.
3303 *
3304 * If an error != NO_ERROR is returned, *pusContext
3305 * will be set to one of the following:
3306 *
3307 * -- 1: boot manager not found
3308 *
3309 * -- 2: primary partitions error
3310 *
3311 * -- 3: secondary partitions error
3312 *
3313 * -- 0: something else.
3314 *
3315 * Based on code (C) Dmitry A. Steklenev.
3316 *
3317 *@@added V0.9.0 [umoeller]
3318 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
3319 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
3320 */
3321
3322APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
3323 PUSHORT pusContext) // out: error context
3324{
3325 APIRET arc = NO_ERROR;
3326
3327 PLVMINFO pLVMInfo = NULL;
3328
3329 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
3330 *ppiTemp = NULL;
3331 USHORT cPartitions = 0; // bootable partition count
3332
3333 if (!ppList)
3334 return (ERROR_INVALID_PARAMETER);
3335
3336 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
3337 {
3338 // LVM installed:
3339 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
3340 &pPartitionInfos, // out: partitions array
3341 &cPartitions); // out: partitions count
3342 // copied to output below
3343
3344 if (arc)
3345 {
3346 // error: start over
3347 doshFreeLVMInfo(pLVMInfo);
3348 CleanPartitionInfos(pPartitionInfos);
3349 pPartitionInfos = NULL;
3350 cPartitions = 0;
3351 }
3352 }
3353
3354 if (arc)
3355 {
3356 // LVM not installed, or failed:
3357 // parse partitions manually
3358 PAR_INFO BmInfo; // BootManager partition
3359 USHORT usBmDisk; // BootManager disk
3360 USHORT cDisks = doshQueryDiskCount(); // physical disks count
3361 USHORT i;
3362
3363 CHAR cLetter = 'C'; // first drive letter
3364
3365 // start over
3366 arc = NO_ERROR;
3367
3368 if (cDisks > 8) // Not above 8 disks
3369 cDisks = 8;
3370
3371 // get boot manager disk and info
3372 if ((arc = doshGetBootManager(&usBmDisk,
3373 NULL,
3374 &BmInfo)) != NO_ERROR)
3375 {
3376 *pusContext = 1;
3377 }
3378 else
3379 {
3380 // on each disk, read primary partitions
3381 for (i = 1; i <= cDisks; i++)
3382 {
3383 if ((arc = GetPrimaryPartitions(&pPartitionInfos,
3384 &ppiTemp,
3385 &cPartitions,
3386 &cLetter,
3387 usBmDisk,
3388 usBmDisk ? &BmInfo : 0,
3389 i)))
3390 {
3391 *pusContext = 2;
3392 }
3393 }
3394
3395 if (!arc && usBmDisk)
3396 {
3397 // boot manager found:
3398 // on each disk, read extended partition
3399 // with logical drives
3400 for (i = 1; i <= cDisks; i++)
3401 {
3402 if ((arc = GetExtendedPartition(&pPartitionInfos,
3403 &ppiTemp,
3404 &cPartitions,
3405 &cLetter,
3406 &BmInfo,
3407 i)))
3408 {
3409 *pusContext = 3;
3410 }
3411 }
3412 }
3413 } // end else if ((arc = doshGetBootManager(&usBmDisk,
3414 } // end else if (!doshQueryLVMInfo(&pLVMInfo))
3415
3416 if (!arc)
3417 {
3418 // no error so far:
3419 *pusContext = 0;
3420
3421 *ppList = NEW(PARTITIONSLIST);
3422 if (!(*ppList))
3423 arc = ERROR_NOT_ENOUGH_MEMORY;
3424 else
3425 {
3426 ZERO(*ppList);
3427
3428 (*ppList)->pPartitionInfo = pPartitionInfos;
3429 (*ppList)->cPartitions = cPartitions;
3430
3431 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
3432 }
3433 }
3434
3435 if (arc)
3436 CleanPartitionInfos(pPartitionInfos);
3437
3438 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
3439
3440 return (arc);
3441}
3442
3443/*
3444 *@@ doshFreePartitionsList:
3445 * this frees the resources allocated by
3446 * doshGetPartitionsList.
3447 *
3448 *@@added V0.9.0 [umoeller]
3449 */
3450
3451APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
3452{
3453 if (!ppList)
3454 return (ERROR_INVALID_PARAMETER);
3455 else
3456 {
3457 CleanPartitionInfos(ppList->pPartitionInfo);
3458 doshFreeLVMInfo(ppList->pLVMInfo);
3459 free(ppList);
3460 }
3461
3462 return (NO_ERROR);
3463}
3464
3465/********************************************************************
3466 *
3467 * LVM declarations
3468 *
3469 ********************************************************************/
3470
3471/*
3472 *@@category: Helpers\Control program helpers\Partitions info\Quick LVM Interface
3473 * functions for transparently interfacing LVM.DLL.
3474 */
3475
3476typedef unsigned char BOOLEAN;
3477typedef unsigned short int CARDINAL16;
3478typedef unsigned long CARDINAL32;
3479typedef unsigned int CARDINAL;
3480typedef unsigned long DoubleWord;
3481
3482#ifdef ADDRESS
3483#undef ADDRESS
3484#endif
3485
3486typedef void* ADDRESS;
3487
3488#pragma pack(1)
3489
3490#define DISK_NAME_SIZE 20
3491#define FILESYSTEM_NAME_SIZE 20
3492#define PARTITION_NAME_SIZE 20
3493#define VOLUME_NAME_SIZE 20
3494
3495/*
3496 *@@ Drive_Control_Record:
3497 * invariant for a disk drive.
3498 *
3499 *@@added V0.9.9 (2001-04-07) [umoeller]
3500 */
3501
3502typedef struct _Drive_Control_Record
3503{
3504 CARDINAL32 Drive_Number; // OS/2 Drive Number for this drive.
3505 CARDINAL32 Drive_Size; // The total number of sectors on the drive.
3506 DoubleWord Drive_Serial_Number; // The serial number assigned to this drive. For info. purposes only.
3507 ADDRESS Drive_Handle; // Handle used for operations on the disk that this record corresponds to.
3508 CARDINAL32 Cylinder_Count; // The number of cylinders on the drive.
3509 CARDINAL32 Heads_Per_Cylinder; // The number of heads per cylinder for this drive.
3510 CARDINAL32 Sectors_Per_Track; // The number of sectors per track for this drive.
3511 BOOLEAN Drive_Is_PRM; // Set to TRUE if this drive is a PRM.
3512 BYTE Reserved[3]; // Alignment.
3513} Drive_Control_Record;
3514
3515/*
3516 *@@ Drive_Control_Array:
3517 * returned by the Get_Drive_Control_Data function
3518 *
3519 *@@added V0.9.9 (2001-04-07) [umoeller]
3520 */
3521
3522typedef struct _Drive_Control_Array
3523{
3524 Drive_Control_Record * Drive_Control_Data; // An array of drive control records.
3525 CARDINAL32 Count; // The number of entries in the array of drive control records.
3526} Drive_Control_Array;
3527
3528/*
3529 *@@ Drive_Information_Record:
3530 * defines the information that can be changed for a specific disk drive.
3531 *
3532 *@@added V0.9.9 (2001-04-07) [umoeller]
3533 */
3534
3535typedef struct _Drive_Information_Record
3536{
3537 CARDINAL32 Total_Available_Sectors; // The number of sectors on the disk which are not currently assigned to a partition.
3538 CARDINAL32 Largest_Free_Block_Of_Sectors; // The number of sectors in the largest contiguous block of available sectors.
3539 BOOLEAN Corrupt_Partition_Table; // If TRUE, then the partitioning information found on the drive is incorrect!
3540 BOOLEAN Unusable; // If TRUE, the drive's MBR is not accessible and the drive can not be partitioned.
3541 BOOLEAN IO_Error; // If TRUE, then the last I/O operation on this drive failed!
3542 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).
3543 char Drive_Name[DISK_NAME_SIZE]; // User assigned name for this disk drive.
3544} Drive_Information_Record;
3545
3546/*
3547 *@@ Partition_Information_Record:
3548 *
3549 *@@added V0.9.9 (2001-04-07) [umoeller]
3550 */
3551
3552typedef struct _Partition_Information_Record
3553{
3554 ADDRESS Partition_Handle;
3555 // The handle used to perform operations on this partition.
3556 ADDRESS Volume_Handle;
3557 // If this partition is part of a volume, this will be the handle of
3558 // the volume. If this partition is NOT part of a volume, then this
3559 // handle will be 0.
3560 ADDRESS Drive_Handle;
3561 // The handle for the drive this partition resides on.
3562 DoubleWord Partition_Serial_Number;
3563 // The serial number assigned to this partition.
3564 CARDINAL32 Partition_Start;
3565 // The LBA of the first sector of the partition.
3566 CARDINAL32 True_Partition_Size;
3567 // The total number of sectors comprising the partition.
3568 CARDINAL32 Usable_Partition_Size;
3569 // The size of the partition as reported to the IFSM. This is the
3570 // size of the partition less any LVM overhead.
3571 CARDINAL32 Boot_Limit;
3572 // The maximum number of sectors from this block of free space that
3573 // can be used to create a bootable partition if you allocate from the
3574 // beginning of the block of free space.
3575 BOOLEAN Spanned_Volume;
3576 // TRUE if this partition is part of a multi-partition volume.
3577 BOOLEAN Primary_Partition;
3578 // True or False. Any non-zero value here indicates that this partition
3579 // is a primary partition. Zero here indicates that this partition is
3580 // a "logical drive" - i.e. it resides inside of an extended partition.
3581 BYTE Active_Flag;
3582 // 80 = Partition is marked as being active.
3583 // 0 = Partition is not active.
3584 BYTE OS_Flag;
3585 // This field is from the partition table. It is known as the OS flag,
3586 // the Partition Type Field, Filesystem Type, and various other names.
3587 // Values of interest
3588 // If this field is: (values are in hex)
3589 // 07 = The partition is a compatibility partition formatted for use
3590 // with an installable filesystem, such as HPFS or JFS.
3591 // 00 = Unformatted partition
3592 // 01 = FAT12 filesystem is in use on this partition.
3593 // 04 = FAT16 filesystem is in use on this partition.
3594 // 0A = OS/2 Boot Manager Partition
3595 // 35 = LVM partition
3596 // 84 = OS/2 FAT16 partition which has been relabeled by Boot Manager to "Hide" it.
3597 BYTE Partition_Type;
3598 // 0 = Free Space
3599 // 1 = LVM Partition (Part of an LVM Volume.)
3600 // 2 = Compatibility Partition
3601 // All other values are reserved for future use.
3602 BYTE Partition_Status;
3603 // 0 = Free Space
3604 // 1 = In Use - i.e. already assigned to a volume.
3605 // 2 = Available - i.e. not currently assigned to a volume.
3606 BOOLEAN On_Boot_Manager_Menu;
3607 // Set to TRUE if this partition is not part of a Volume yet is on the
3608 // Boot Manager Menu.
3609 BYTE Reserved;
3610 // Alignment.
3611 char Volume_Drive_Letter;
3612 // The drive letter assigned to the volume that this partition is a part of.
3613 char Drive_Name[DISK_NAME_SIZE];
3614 // User assigned name for this disk drive.
3615 char File_System_Name[FILESYSTEM_NAME_SIZE];
3616 // The name of the filesystem in use on this partition, if it is known.
3617 char Partition_Name[PARTITION_NAME_SIZE];
3618 // The user assigned name for this partition.
3619 char Volume_Name[VOLUME_NAME_SIZE];
3620 // If this partition is part of a volume, then this will be the
3621 // name of the volume that this partition is a part of. If this
3622 // record represents free space, then the Volume_Name will be
3623 // "FREE SPACE xx", where xx is a unique numeric ID generated by
3624 // LVM.DLL. Otherwise it will be an empty string.
3625} Partition_Information_Record;
3626
3627// The following defines are for use with the Partition_Type field in the
3628// Partition_Information_Record.
3629#define FREE_SPACE_PARTITION 0
3630#define LVM_PARTITION 1
3631#define COMPATIBILITY_PARTITION 2
3632
3633// The following defines are for use with the Partition_Status field in the
3634// Partition_Information_Record.
3635#define PARTITION_IS_IN_USE 1
3636#define PARTITION_IS_AVAILABLE 2
3637#define PARTITION_IS_FREE_SPACE 0
3638
3639/*
3640 *@@ Partition_Information_Array:
3641 * returned by various functions in the LVM Engine.
3642 *
3643 *@@added V0.9.9 (2001-04-07) [umoeller]
3644 */
3645
3646typedef struct _Partition_Information_Array
3647{
3648 Partition_Information_Record * Partition_Array; // An array of Partition_Information_Records.
3649 CARDINAL32 Count; // The number of entries in the Partition_Array.
3650} Partition_Information_Array;
3651
3652/*
3653 *@@ Volume_Information_Record:
3654 * variable information for a volume.
3655 *
3656 *@@added V0.9.9 (2001-04-07) [umoeller]
3657 */
3658
3659typedef struct _Volume_Information_Record
3660{
3661 CARDINAL32 Volume_Size;
3662 // The number of sectors comprising the volume.
3663 CARDINAL32 Partition_Count;
3664 // The number of partitions which comprise this volume.
3665 CARDINAL32 Drive_Letter_Conflict;
3666 // 0 indicates that the drive letter preference for this volume is unique.
3667 // 1 indicates that the drive letter preference for this volume
3668 // is not unique, but this volume got its preferred drive letter anyway.
3669 // 2 indicates that the drive letter preference for this volume
3670 // is not unique, and this volume did NOT get its preferred drive letter.
3671 // 4 indicates that this volume is currently "hidden" - i.e. it has
3672 // no drive letter preference at the current time.
3673 BOOLEAN Compatibility_Volume;
3674 // TRUE if this is for a compatibility volume, FALSE otherwise.
3675 BOOLEAN Bootable;
3676 // Set to TRUE if this volume appears on the Boot Manager menu, or if it is
3677 // a compatibility volume and its corresponding partition is the first active
3678 // primary partition on the first drive.
3679 char Drive_Letter_Preference;
3680 // The drive letter that this volume desires to be.
3681 char Current_Drive_Letter;
3682 // The drive letter currently used to access this volume.
3683 // May be different than Drive_Letter_Preference if there was a conflict ( i.e. Drive_Letter_Preference
3684 // is already in use by another volume ).
3685 char Initial_Drive_Letter;
3686 // The drive letter assigned to this volume by the operating system
3687 // when LVM was started. This may be different from the
3688 // Drive_Letter_Preference if there were conflicts, and
3689 // may be different from the Current_Drive_Letter. This
3690 // will be 0x0 if the Volume did not exist when the LVM Engine
3691 // was opened (i.e. it was created during this LVM session).
3692 BOOLEAN New_Volume;
3693 // Set to FALSE if this volume existed before the LVM Engine was
3694 // opened. Set to TRUE if this volume was created after the LVM
3695 // Engine was opened.
3696 BYTE Status;
3697 // 0 = None.
3698 // 1 = Bootable
3699 // 2 = Startable
3700 // 3 = Installable.
3701 BYTE Reserved_1;
3702 char Volume_Name[VOLUME_NAME_SIZE];
3703 // The user assigned name for this volume.
3704 char File_System_Name[FILESYSTEM_NAME_SIZE];
3705 // The name of the filesystem in use on this partition, if it
3706 // is known.
3707} Volume_Information_Record;
3708
3709#pragma pack()
3710
3711/********************************************************************
3712 *
3713 * Quick LVM Interface API
3714 *
3715 ********************************************************************/
3716
3717/*
3718 *@@ LVMINFOPRIVATE:
3719 * private structure used by doshQueryLVMInfo.
3720 * This is what the LVMINFO pointer really
3721 * points to.
3722 *
3723 *@@added V0.9.9 (2001-04-07) [umoeller]
3724 */
3725
3726typedef struct _LVMINFOPRIVATE
3727{
3728 LVMINFO LVMInfo; // public structure (dosh.h)
3729
3730 // function pointers resolved from LVM.DLL
3731
3732 void (* _System Open_LVM_Engine)(BOOLEAN Ignore_CHS,
3733 CARDINAL32 *Error_Code);
3734
3735 void (* _System Free_Engine_Memory)(ADDRESS Object);
3736
3737 void (* _System Close_LVM_Engine)(void);
3738
3739 Drive_Control_Array (* _System
3740 Get_Drive_Control_Data)(CARDINAL32 *Error_Code);
3741
3742 Drive_Information_Record (* _System
3743 Get_Drive_Status)(ADDRESS Drive_Handle,
3744 CARDINAL32 *Error_Code);
3745
3746 Partition_Information_Array (* _System
3747 Get_Partitions)(ADDRESS Handle,
3748 CARDINAL32 *Error_Code);
3749
3750 Volume_Information_Record (*_System
3751 Get_Volume_Information)(ADDRESS Volume_Handle,
3752 CARDINAL32 *Error_Code);
3753
3754} LVMINFOPRIVATE, *PLVMINFOPRIVATE;
3755
3756#define LVM_ERROR_FIRST 20000
3757
3758/*
3759 *@@ doshQueryLVMInfo:
3760 * creates an LVMINFO structure if LVM is installed.
3761 * Returns that structure (which the caller must free
3762 * using doshFreeLVMInfo) or NULL if LVM.DLL was not
3763 * found along the LIBPATH.
3764 *
3765 *@@added V0.9.9 (2001-04-07) [umoeller]
3766 */
3767
3768APIRET doshQueryLVMInfo(PLVMINFO *ppLVMInfo)
3769{
3770 APIRET arc = NO_ERROR;
3771 CHAR szError[100];
3772 PLVMINFOPRIVATE pLVMInfo = NULL;
3773 HMODULE hmodLVM = NULLHANDLE;
3774
3775 if (!(arc = DosLoadModule(szError,
3776 sizeof(szError),
3777 "LVM",
3778 &hmodLVM)))
3779 {
3780 // got LVM.DLL:
3781 pLVMInfo = NEW(LVMINFOPRIVATE);
3782 if (!pLVMInfo)
3783 arc = ERROR_NOT_ENOUGH_MEMORY;
3784 else
3785 {
3786 // array of function pointers to be resolved from LVM.DLL
3787 RESOLVEFUNCTION aFunctions[] =
3788 {
3789 "Open_LVM_Engine", (PFN*)&pLVMInfo->Open_LVM_Engine,
3790 "Free_Engine_Memory", (PFN*)&pLVMInfo->Free_Engine_Memory,
3791 "Close_LVM_Engine", (PFN*)&pLVMInfo->Close_LVM_Engine,
3792 "Get_Drive_Control_Data", (PFN*)&pLVMInfo->Get_Drive_Control_Data,
3793 "Get_Drive_Status", (PFN*)&pLVMInfo->Get_Drive_Status,
3794 "Get_Partitions", (PFN*)&pLVMInfo->Get_Partitions,
3795 "Get_Volume_Information", (PFN*)&pLVMInfo->Get_Volume_Information
3796 };
3797 ULONG ul;
3798
3799 ZERO(pLVMInfo);
3800
3801 pLVMInfo->LVMInfo.hmodLVM = hmodLVM;
3802
3803 // now resolve function pointers
3804 for (ul = 0;
3805 ul < ARRAYITEMCOUNT(aFunctions);
3806 ul++)
3807 {
3808 PRESOLVEFUNCTION pFuncThis = &aFunctions[ul];
3809 arc = DosQueryProcAddr(hmodLVM,
3810 0, // ordinal, ignored
3811 (PSZ)pFuncThis->pcszFunctionName,
3812 pFuncThis->ppFuncAddress);
3813 if (!pFuncThis->ppFuncAddress)
3814 arc = ERROR_INVALID_NAME;
3815
3816 if (arc)
3817 break;
3818 }
3819 }
3820 }
3821
3822 if (arc)
3823 doshFreeLVMInfo((PLVMINFO)pLVMInfo);
3824 else
3825 *ppLVMInfo = (PLVMINFO)pLVMInfo;
3826
3827 return (arc);
3828}
3829
3830/*
3831 *@@ doshReadLVMPartitions:
3832 * using the LVMINFO parameter from doshQueryLVMInfo,
3833 * builds an array of PARTITIONINFO structures with
3834 * the data returned from LVM.DLL.
3835 *
3836 *@@added V0.9.9 (2001-04-07) [umoeller]
3837 */
3838
3839APIRET doshReadLVMPartitions(PLVMINFO pInfo, // in: LVM info
3840 PPARTITIONINFO *ppPartitionInfo, // out: partitions array
3841 PUSHORT pcPartitions) // out: partitions count
3842{
3843 APIRET arc = NO_ERROR;
3844 CARDINAL32 Error = 0;
3845
3846 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
3847 *ppiTemp = NULL;
3848 USHORT cPartitions = 0; // bootable partition count
3849 PLVMINFOPRIVATE pLVMInfo = (PLVMINFOPRIVATE)pInfo;
3850
3851 _Pmpf((__FUNCTION__ ": entering"));
3852
3853 if (!pLVMInfo)
3854 return (ERROR_INVALID_PARAMETER);
3855
3856 // initialize LVM engine
3857 pLVMInfo->Open_LVM_Engine(TRUE,
3858 &Error);
3859
3860 _Pmpf((" Open_LVM_Engine Error: %d"));
3861
3862 if (!Error)
3863 {
3864 Drive_Control_Array DCA = pLVMInfo->Get_Drive_Control_Data(&Error);
3865 // member records to be freed
3866
3867 _Pmpf((" Get_Drive_Control_Data Error: %d, drive count: %d", Error, DCA.Count));
3868
3869 if ( (!Error)
3870 && (DCA.Count)
3871 )
3872 {
3873 // DCA.Drive_Control_Data now contains drive information records;
3874 // this must be freed
3875 ULONG ulDisk;
3876
3877 for (ulDisk = 0;
3878 ulDisk < DCA.Count;
3879 ulDisk++)
3880 {
3881 Drive_Control_Record *pDriveControlRecord
3882 = &DCA.Drive_Control_Data[ulDisk];
3883 ADDRESS hDrive = pDriveControlRecord->Drive_Handle;
3884
3885 Drive_Information_Record pDriveInfoRecord
3886 = pLVMInfo->Get_Drive_Status(hDrive,
3887 &Error);
3888
3889 _Pmpf((" drive %d Get_Drive_Status Error: %d", ulDisk, Error));
3890
3891 if (!Error)
3892 {
3893 Partition_Information_Array PIA
3894 = pLVMInfo->Get_Partitions(hDrive,
3895 &Error);
3896
3897 _Pmpf((" Get_Partitions Error: %d", Error));
3898
3899 if (!Error)
3900 {
3901 // PIA.Partition_Array now contains
3902 // Partition_Information_Record; must be freed
3903
3904 // now go thru partitions of this drive
3905 ULONG ulPart;
3906 for (ulPart = 0;
3907 ulPart < PIA.Count;
3908 ulPart++)
3909 {
3910 Partition_Information_Record *pPartition
3911 = &PIA.Partition_Array[ulPart];
3912 Volume_Information_Record VolumeInfo;
3913
3914 const char *pcszBootName = NULL; // for now
3915 BOOL fBootable = FALSE;
3916
3917 if (pPartition->Volume_Handle)
3918 {
3919 // this partition is part of a volume:
3920 // only then can it be bootable...
3921 // get the volume info
3922 VolumeInfo
3923 = pLVMInfo->Get_Volume_Information(pPartition->Volume_Handle,
3924 &Error);
3925 pcszBootName = VolumeInfo.Volume_Name;
3926
3927 fBootable = (VolumeInfo.Status == 1);
3928 }
3929
3930
3931 if (arc = AppendPartition(&pPartitionInfos,
3932 &ppiTemp,
3933 &cPartitions,
3934 ulDisk + 1,
3935 pcszBootName,
3936 pPartition->Volume_Drive_Letter,
3937 pPartition->OS_Flag, // FS type
3938 pPartition->Primary_Partition,
3939 fBootable,
3940 pPartition->True_Partition_Size))
3941 break;
3942 }
3943
3944 // clean up partitions
3945 pLVMInfo->Free_Engine_Memory(PIA.Partition_Array);
3946 }
3947 }
3948 else
3949 // error:
3950 break;
3951 }
3952
3953 // clean up drive data
3954 pLVMInfo->Free_Engine_Memory(DCA.Drive_Control_Data);
3955 }
3956 }
3957
3958 // close LVM
3959 pLVMInfo->Close_LVM_Engine();
3960
3961 if (Error)
3962 {
3963 // if we got an error, return it with the
3964 // LVM error offset
3965 arc = LVM_ERROR_FIRST + Error;
3966
3967 CleanPartitionInfos(pPartitionInfos);
3968 }
3969
3970 if (!arc)
3971 {
3972 *ppPartitionInfo = pPartitionInfos;
3973 *pcPartitions = cPartitions;
3974 }
3975
3976 _Pmpf((__FUNCTION__ ": exiting, arg = %d", arc));
3977
3978 return (arc);
3979}
3980
3981/*
3982 *@@ doshFreeLVMInfo:
3983 *
3984 *@@added V0.9.9 (2001-04-07) [umoeller]
3985 */
3986
3987VOID doshFreeLVMInfo(PLVMINFO pInfo)
3988{
3989 if (pInfo)
3990 {
3991 if (pInfo->hmodLVM)
3992 DosFreeModule(pInfo->hmodLVM);
3993
3994 free(pInfo);
3995 }
3996}
Note: See TracBrowser for help on using the repository browser.