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

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

Various fixes for V0.9.10.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 137.3 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 *@@category: Helpers\Control program helpers\Partitions info
2365 * functions for retrieving partition information directly
2366 * from the partition tables on the disk. See doshGetPartitionsList.
2367 */
2368
2369/********************************************************************
2370 *
2371 * Partition functions
2372 *
2373 ********************************************************************/
2374
2375/*
2376 *@@ doshQueryDiskCount:
2377 * returns the no. of physical disks installed
2378 * on the system.
2379 *
2380 * Based on code (C) Dmitry A. Steklenev.
2381 *
2382 *@@added V0.9.0 [umoeller]
2383 */
2384
2385UINT doshQueryDiskCount(VOID)
2386{
2387 USHORT count = 0;
2388
2389 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &count, 2, 0, 0);
2390 return (count);
2391}
2392
2393/*
2394 *@@ doshReadSector:
2395 * reads a physical disk sector.
2396 *
2397 * If NO_ERROR is returned, the sector contents
2398 * have been stored in *buff.
2399 *
2400 * Based on code (C) Dmitry A. Steklenev.
2401 *
2402 *@@added V0.9.0 [umoeller]
2403 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
2404 */
2405
2406APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
2407 void *buff,
2408 USHORT head,
2409 USHORT cylinder,
2410 USHORT sector)
2411{
2412 APIRET arc;
2413 HFILE dh = 0;
2414 char dn[256];
2415
2416 sprintf(dn, "%u:", disk);
2417 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
2418 {
2419 TRACKLAYOUT DiskIOParm;
2420 ULONG IOCtlDataLength = sizeof(DiskIOParm);
2421 ULONG IOCtlParmLength = 512;
2422
2423 DiskIOParm.bCommand = 0;
2424 DiskIOParm.usHead = head;
2425 DiskIOParm.usCylinder = cylinder;
2426 DiskIOParm.usFirstSector = 0;
2427 DiskIOParm.cSectors = 1;
2428 DiskIOParm.TrackTable[0].usSectorNumber = sector;
2429 DiskIOParm.TrackTable[0].usSectorSize = 512;
2430
2431 arc = DosDevIOCtl(dh,
2432 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
2433 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
2434 buff , IOCtlDataLength, &IOCtlDataLength);
2435
2436 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
2437 }
2438
2439 return (arc);
2440}
2441
2442/*
2443 *@@ doshType2FSName:
2444 * this returns a static, zero-terminated string
2445 * for the given FS type. This is always 7 bytes
2446 * in length.
2447 *
2448 * Values for operating system indicator:
2449 * -- 00h empty
2450 * -- 01h DOS 12-bit FAT
2451 * -- 02h XENIX root file system
2452 * -- 03h XENIX /usr file system (obsolete)
2453 * -- 04h DOS 16-bit FAT (up to 32M)
2454 * -- 05h DOS 3.3+ extended partition
2455 * -- 06h DOS 3.31+ Large File System (16-bit FAT, over 32M)
2456 * -- 07h QNX
2457 * -- 07h OS/2 HPFS
2458 * -- 07h Windows NT NTFS
2459 * -- 07h Advanced Unix
2460 * -- 08h OS/2 (v1.0-1.3 only)
2461 * -- 08h AIX bootable partition, SplitDrive
2462 * -- 08h Commodore DOS
2463 * -- 08h DELL partition spanning multiple drives
2464 * -- 09h AIX data partition
2465 * -- 09h Coherent filesystem
2466 * -- 0Ah OS/2 Boot Manager
2467 * -- 0Ah OPUS
2468 * -- 0Ah Coherent swap partition
2469 * -- 0Bh Windows95 with 32-bit FAT
2470 * -- 0Ch Windows95 with 32-bit FAT (using LBA-mode INT 13 extensions)
2471 * -- 0Eh logical-block-addressable VFAT (same as 06h but using LBA-mode INT 13)
2472 * -- 0Fh logical-block-addressable VFAT (same as 05h but using LBA-mode INT 13)
2473 * -- 10h OPUS
2474 * -- 11h OS/2 Boot Manager hidden 12-bit FAT partition
2475 * -- 12h Compaq Diagnostics partition
2476 * -- 14h (resulted from using Novell DOS 7.0 FDISK to delete Linux Native part)
2477 * -- 14h OS/2 Boot Manager hidden sub-32M 16-bit FAT partition
2478 * -- 16h OS/2 Boot Manager hidden over-32M 16-bit FAT partition
2479 * -- 17h OS/2 Boot Manager hidden HPFS partition
2480 * -- 18h AST special Windows swap file ("Zero-Volt Suspend" partition)
2481 * -- 21h officially listed as reserved
2482 * -- 23h officially listed as reserved
2483 * -- 24h NEC MS-DOS 3.x
2484 * -- 26h officially listed as reserved
2485 * -- 31h officially listed as reserved
2486 * -- 33h officially listed as reserved
2487 * -- 34h officially listed as reserved
2488 * -- 36h officially listed as reserved
2489 * -- 38h Theos
2490 * -- 3Ch PowerQuest PartitionMagic recovery partition
2491 * -- 40h VENIX 80286
2492 * -- 41h Personal RISC Boot
2493 * -- 42h SFS (Secure File System) by Peter Gutmann
2494 * -- 50h OnTrack Disk Manager, read-only partition
2495 * -- 51h OnTrack Disk Manager, read/write partition
2496 * -- 51h NOVEL
2497 * -- 52h CP/M
2498 * -- 52h Microport System V/386
2499 * -- 53h OnTrack Disk Manager, write-only partition???
2500 * -- 54h OnTrack Disk Manager (DDO)
2501 * -- 56h GoldenBow VFeature
2502 * -- 61h SpeedStor
2503 * -- 63h Unix SysV/386, 386/ix
2504 * -- 63h Mach, MtXinu BSD 4.3 on Mach
2505 * -- 63h GNU HURD
2506 * -- 64h Novell NetWare 286
2507 * -- 65h Novell NetWare (3.11)
2508 * -- 67h Novell
2509 * -- 68h Novell
2510 * -- 69h Novell
2511 * -- 70h DiskSecure Multi-Boot
2512 * -- 71h officially listed as reserved
2513 * -- 73h officially listed as reserved
2514 * -- 74h officially listed as reserved
2515 * -- 75h PC/IX
2516 * -- 76h officially listed as reserved
2517 * -- 80h Minix v1.1 - 1.4a
2518 * -- 81h Minix v1.4b+
2519 * -- 81h Linux
2520 * -- 81h Mitac Advanced Disk Manager
2521 * -- 82h Linux Swap partition
2522 * -- 82h Prime
2523 * -- 83h Linux native file system (ext2fs/xiafs)
2524 * -- 84h OS/2-renumbered type 04h partition (related to hiding DOS C: drive)
2525 * -- 86h FAT16 volume/stripe set (Windows NT)
2526 * -- 87h HPFS Fault-Tolerant mirrored partition
2527 * -- 87h NTFS volume/stripe set
2528 * -- 93h Amoeba file system
2529 * -- 94h Amoeba bad block table
2530 * -- A0h Phoenix NoteBIOS Power Management "Save-to-Disk" partition
2531 * -- A1h officially listed as reserved
2532 * -- A3h officially listed as reserved
2533 * -- A4h officially listed as reserved
2534 * -- A5h FreeBSD, BSD/386
2535 * -- A6h officially listed as reserved
2536 * -- B1h officially listed as reserved
2537 * -- B3h officially listed as reserved
2538 * -- B4h officially listed as reserved
2539 * -- B6h officially listed as reserved
2540 * -- B7h BSDI file system (secondarily swap)
2541 * -- B8h BSDI swap partition (secondarily file system)
2542 * -- C1h DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT partition
2543 * -- C4h DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT partition
2544 * -- C6h DR DOS 6.0 LOGIN.EXE-secured Huge partition
2545 * -- C6h corrupted FAT16 volume/stripe set (Windows NT)
2546 * -- C7h Syrinx Boot
2547 * -- C7h corrupted NTFS volume/stripe set
2548 * -- D8h CP/M-86
2549 * -- DBh CP/M, Concurrent CP/M, Concurrent DOS
2550 * -- DBh CTOS (Convergent Technologies OS)
2551 * -- E1h SpeedStor 12-bit FAT extended partition
2552 * -- E3h DOS read-only
2553 * -- E3h Storage Dimensions
2554 * -- E4h SpeedStor 16-bit FAT extended partition
2555 * -- E5h officially listed as reserved
2556 * -- E6h officially listed as reserved
2557 * -- F1h Storage Dimensions
2558 * -- F2h DOS 3.3+ secondary partition
2559 * -- F3h officially listed as reserved
2560 * -- F4h SpeedStor
2561 * -- F4h Storage Dimensions
2562 * -- F6h officially listed as reserved
2563 * -- FEh LANstep
2564 * -- FEh IBM PS/2 IML
2565 * -- FFh Xenix bad block table
2566 *
2567 * Note: for partition type 07h, one should inspect the partition boot record
2568 * for the actual file system type
2569 *
2570 * Based on code (C) Dmitry A. Steklenev.
2571 *
2572 *@@added V0.9.0 [umoeller]
2573 */
2574
2575const char* doshType2FSName(unsigned char bFSType) // in: FS type
2576{
2577 PSZ zFSName = NULL;
2578
2579 switch (bFSType)
2580 {
2581 case PAR_UNUSED:
2582 zFSName = "UNUSED ";
2583 break;
2584 case PAR_FAT12SMALL:
2585 zFSName = "FAT-12 ";
2586 break;
2587 case PAR_XENIXROOT:
2588 zFSName = "XENIX ";
2589 break;
2590 case PAR_XENIXUSER:
2591 zFSName = "XENIX ";
2592 break;
2593 case PAR_FAT16SMALL:
2594 zFSName = "FAT-16 ";
2595 break;
2596 case PAR_EXTENDED:
2597 zFSName = "EXTEND ";
2598 break;
2599 case PAR_FAT16BIG:
2600 zFSName = "BIGDOS ";
2601 break;
2602 case PAR_HPFS:
2603 zFSName = "HPFS ";
2604 break;
2605 case PAR_AIXBOOT:
2606 zFSName = "AIX ";
2607 break;
2608 case PAR_AIXDATA:
2609 zFSName = "AIX ";
2610 break;
2611 case PAR_BOOTMANAGER:
2612 zFSName = "BOOTMNG";
2613 break;
2614 case PAR_WINDOWS95:
2615 zFSName = "WIN95 ";
2616 break;
2617 case PAR_WINDOWS95LB:
2618 zFSName = "WIN95 ";
2619 break;
2620 case PAR_VFAT16BIG:
2621 zFSName = "VFAT ";
2622 break;
2623 case PAR_VFAT16EXT:
2624 zFSName = "VFAT ";
2625 break;
2626 case PAR_OPUS:
2627 zFSName = "OPUS ";
2628 break;
2629 case PAR_HID12SMALL:
2630 zFSName = "FAT-12*";
2631 break;
2632 case PAR_COMPAQDIAG:
2633 zFSName = "COMPAQ ";
2634 break;
2635 case PAR_HID16SMALL:
2636 zFSName = "FAT-16*";
2637 break;
2638 case PAR_HID16BIG:
2639 zFSName = "BIGDOS*";
2640 break;
2641 case PAR_HIDHPFS:
2642 zFSName = "HPFS* ";
2643 break;
2644 case PAR_WINDOWSSWP:
2645 zFSName = "WINSWAP";
2646 break;
2647 case PAR_NECDOS:
2648 zFSName = "NECDOS ";
2649 break;
2650 case PAR_THEOS:
2651 zFSName = "THEOS ";
2652 break;
2653 case PAR_VENIX:
2654 zFSName = "VENIX ";
2655 break;
2656 case PAR_RISCBOOT:
2657 zFSName = "RISC ";
2658 break;
2659 case PAR_SFS:
2660 zFSName = "SFS ";
2661 break;
2662 case PAR_ONTRACK:
2663 zFSName = "ONTRACK";
2664 break;
2665 case PAR_ONTRACKEXT:
2666 zFSName = "ONTRACK";
2667 break;
2668 case PAR_CPM:
2669 zFSName = "CP/M ";
2670 break;
2671 case PAR_UNIXSYSV:
2672 zFSName = "UNIX ";
2673 break;
2674 case PAR_NOVELL_64:
2675 zFSName = "NOVELL ";
2676 break;
2677 case PAR_NOVELL_65:
2678 zFSName = "NOVELL ";
2679 break;
2680 case PAR_NOVELL_67:
2681 zFSName = "NOVELL ";
2682 break;
2683 case PAR_NOVELL_68:
2684 zFSName = "NOVELL ";
2685 break;
2686 case PAR_NOVELL_69:
2687 zFSName = "NOVELL ";
2688 break;
2689 case PAR_PCIX:
2690 zFSName = "PCIX ";
2691 break;
2692 case PAR_MINIX:
2693 zFSName = "MINIX ";
2694 break;
2695 case PAR_LINUX:
2696 zFSName = "LINUX ";
2697 break;
2698 case PAR_LINUXSWAP:
2699 zFSName = "LNXSWP ";
2700 break;
2701 case PAR_LINUXFILE:
2702 zFSName = "LINUX ";
2703 break;
2704 case PAR_FREEBSD:
2705 zFSName = "FREEBSD";
2706 break;
2707 case PAR_BBT:
2708 zFSName = "BBT ";
2709 break;
2710
2711 default:
2712 zFSName = " ";
2713 break;
2714 }
2715 return zFSName;
2716}
2717
2718/*
2719 * AppendPartition:
2720 * this appends the given partition information to
2721 * the given partition list. To do this, a new
2722 * PARTITIONINFO structure is created and appended
2723 * in a list (managed thru the PARTITIONINFO.pNext
2724 * items).
2725 *
2726 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
2727 * With each call of this function, this pointer is advanced
2728 * to point to the newly created PARTITIONINFO, so before
2729 * calling this function for the first time,
2730 *
2731 * Based on code (C) Dmitry A. Steklenev.
2732 *
2733 *@@added V0.9.0 [umoeller]
2734 */
2735
2736APIRET AppendPartition(PARTITIONINFO **pppiFirst,
2737 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
2738 PUSHORT posCount, // in/out: partition count
2739 BYTE bDisk, // in: disk of partition
2740 const char *pszBootName, // in: boot partition name
2741 CHAR cLetter, // in/out: drive letter
2742 BYTE bFsType, // in: file system type
2743 BOOL fPrimary, // in: primary?
2744 BOOL fBootable,
2745 ULONG ulSectors) // in: no. of sectors
2746{
2747 APIRET arc = NO_ERROR;
2748 PPARTITIONINFO ppiNew = NEW(PARTITIONINFO);
2749 if (ppiNew)
2750 {
2751 ZERO(ppiNew);
2752
2753 // store data
2754 ppiNew->bDisk = bDisk;
2755 if ((fBootable) && (pszBootName) )
2756 {
2757 memcpy(ppiNew->szBootName, pszBootName, 8);
2758 ppiNew->szBootName[8] = 0;
2759 }
2760 else
2761 ppiNew->szBootName[0] = 0;
2762 ppiNew->cLetter = cLetter;
2763 ppiNew->bFSType = bFsType;
2764 strcpy(ppiNew->szFSType,
2765 doshType2FSName(bFsType));
2766 ppiNew->fPrimary = fPrimary;
2767 ppiNew->fBootable = fBootable;
2768 ppiNew->ulSize = ulSectors / 2048;
2769
2770 ppiNew->pNext = NULL;
2771
2772 (*posCount)++;
2773
2774 if (*pppiFirst == (PPARTITIONINFO)NULL)
2775 {
2776 // first call:
2777 *pppiFirst = ppiNew;
2778 *pppiThis = ppiNew;
2779 }
2780 else
2781 {
2782 // append to list
2783 (**pppiThis).pNext = ppiNew;
2784 *pppiThis = ppiNew;
2785 }
2786 }
2787 else
2788 arc = ERROR_NOT_ENOUGH_MEMORY;
2789
2790 return (arc);
2791}
2792
2793// Sector and Cylinder values are actually 6 bits and 10 bits:
2794//
2795// 1 1 1 1 1 1
2796// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
2797// ³c c c c c c c c C c S s s s s s³
2798// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
2799//
2800// The high two bits of the second byte are used as the high bits
2801// of a 10-bit value. This allows for as many as 1024 cylinders
2802// and 64 sectors per cylinder.
2803
2804/*
2805 * GetCyl:
2806 * get cylinder number.
2807 *
2808 * Based on code (C) Dmitry A. Steklenev.
2809 *
2810 *@@added V0.9.0 [umoeller]
2811 */
2812
2813static USHORT GetCyl(USHORT rBeginSecCyl)
2814{
2815 return ( (rBeginSecCyl & 0x00C0) << 2)
2816 + ((rBeginSecCyl & 0xFF00) >> 8);
2817}
2818
2819/*
2820 * GetSec:
2821 * get sector number.
2822 *
2823 * Based on code (C) Dmitry A. Steklenev.
2824 *
2825 *@@added V0.9.0 [umoeller]
2826 */
2827
2828static USHORT GetSec(USHORT rBeginSecCyl)
2829{
2830 return rBeginSecCyl & 0x003F;
2831}
2832
2833/*
2834 *@@ doshGetBootManager:
2835 * this goes thru the master boot records on all
2836 * disks to find the boot manager partition.
2837 *
2838 * Returns:
2839 *
2840 * -- NO_ERROR: boot manager found; in that case,
2841 * information about the boot manager
2842 * is written into *pusDisk, *pusPart,
2843 * *BmInfo. Any of these pointers can
2844 * be NULL if you're not interested.
2845 *
2846 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
2847 *
2848 * Based on code (C) Dmitry A. Steklenev.
2849 *
2850 *@@added V0.9.0 [umoeller]
2851 */
2852
2853APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
2854 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
2855 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
2856{
2857 APIRET arc = NO_ERROR;
2858 USHORT count = doshQueryDiskCount(); // Physical disk number
2859 MBR_INFO MBoot; // Master Boot
2860 USHORT usDisk;
2861
2862 if (count > 8) // Not above 8 disks
2863 count = 8;
2864
2865 for (usDisk = 1; usDisk <= count; usDisk++)
2866 {
2867 USHORT usPrim = 0;
2868
2869 // for each disk, read the MBR, which has the
2870 // primary partitions
2871 if ((arc = doshReadSector(usDisk,
2872 &MBoot,
2873 0, // head
2874 0, // cylinder
2875 1))) // sector
2876 return (arc);
2877
2878 // scan primary partitions for whether
2879 // BootManager partition exists
2880 for (usPrim = 0; usPrim < 4; usPrim++)
2881 {
2882 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
2883 {
2884 // this is boot manager:
2885 if (pBmInfo)
2886 *pBmInfo = MBoot.sPrtnInfo[usPrim];
2887 if (pusPart)
2888 *pusPart = usPrim;
2889 if (pusDisk)
2890 *pusDisk = usDisk;
2891 // stop scanning
2892 return (NO_ERROR);
2893 }
2894 }
2895 }
2896
2897 return (ERROR_NOT_SUPPORTED);
2898}
2899
2900/*
2901 * GetPrimaryPartitions:
2902 * this returns the primary partitions.
2903 *
2904 * This gets called from doshGetPartitionsList.
2905 *
2906 * Returns:
2907 *
2908 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
2909 *
2910 * Based on code (C) Dmitry A. Steklenev.
2911 *
2912 *@@added V0.9.0 [umoeller]
2913 */
2914
2915APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
2916 PARTITIONINFO **pppiThis,
2917 PUSHORT posCount, // in/out: partition count
2918 PCHAR pcLetter, // in/out: drive letter counter
2919 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
2920 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
2921 UINT iDisk) // in: system's physical disk count
2922{
2923 APIRET arc = NO_ERROR;
2924
2925 if (!pBmInfo)
2926 arc = ERROR_INVALID_PARAMETER;
2927 else
2928 {
2929 SYS_INFO MName[32]; // Name Space from Boot Manager
2930 memset(&MName, 0, sizeof(MName));
2931
2932 // read boot manager name table;
2933 // this is in the boot manager primary partition
2934 // at sector offset 3 (?!?)
2935 if (!(arc = doshReadSector(BmDisk,
2936 &MName,
2937 // head, cylinder, sector of bmgr primary partition:
2938 pBmInfo->bBeginHead,
2939 GetCyl(pBmInfo->rBeginSecCyl),
2940 GetSec(pBmInfo->rBeginSecCyl) + 3)))
2941 {
2942 // got bmgr name table:
2943 MBR_INFO MBoot; // Master Boot
2944 USHORT i;
2945
2946 // read master boot record of this disk
2947 if (!(arc = doshReadSector(iDisk,
2948 &MBoot,
2949 0, // head
2950 0, // cylinder
2951 1))) // sector
2952 {
2953 for (i = 0;
2954 i < 4; // there can be only four primary partitions
2955 i++)
2956 {
2957 // skip unused partition, BootManager or Extended partition
2958 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
2959 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER) // skip boot manager
2960 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_EXTENDED) // skip extended
2961 )
2962 {
2963 BOOL fBootable = ( (pBmInfo)
2964 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
2965 );
2966 // store this partition
2967 if ((arc = AppendPartition(pppiFirst,
2968 pppiThis,
2969 posCount,
2970 iDisk,
2971 (fBootable)
2972 ? (char*)&MName[(iDisk - 1) * 4 + i].name
2973 : "",
2974 *pcLetter,
2975 MBoot.sPrtnInfo[i].bFileSysCode,
2976 TRUE, // primary
2977 fBootable,
2978 MBoot.sPrtnInfo[i].lTotalSects)))
2979 return (arc);
2980 }
2981 }
2982 }
2983 }
2984 }
2985
2986 return (arc);
2987}
2988
2989/*
2990 * GetLogicalDrives:
2991 * this returns info for the logical drives
2992 * in the extended partition. This gets called
2993 * from GetExtendedPartition.
2994 *
2995 * This gets called from GetExtendedPartition.
2996 *
2997 * Based on code (C) Dmitry A. Steklenev.
2998 *
2999 *@@added V0.9.0 [umoeller]
3000 */
3001
3002APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
3003 PARTITIONINFO **pppiThis,
3004 PUSHORT posCount,
3005 PCHAR pcLetter,
3006 PAR_INFO* PrInfo, // in: MBR entry of extended partition
3007 UINT PrDisk,
3008 PAR_INFO* BmInfo)
3009{
3010 APIRET arc = NO_ERROR;
3011 EXT_INFO MBoot; // Master Boot
3012 USHORT i;
3013
3014 if ((arc = doshReadSector(PrDisk,
3015 &MBoot,
3016 PrInfo->bBeginHead,
3017 GetCyl(PrInfo->rBeginSecCyl),
3018 GetSec(PrInfo->rBeginSecCyl))))
3019 return (arc);
3020
3021 for (i = 0; i < 4; i++)
3022 {
3023 // skip unused partition or BootManager partition
3024 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
3025 && (MBoot.sPrtnInfo[i].bFileSysCode != PAR_BOOTMANAGER)
3026 )
3027 {
3028 BOOL fBootable = FALSE;
3029 BOOL fAssignLetter = FALSE;
3030
3031 // special work around extended partition
3032 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
3033 {
3034 if ((arc = GetLogicalDrives(pppiFirst,
3035 pppiThis,
3036 posCount,
3037 pcLetter,
3038 &MBoot.sPrtnInfo[i],
3039 PrDisk,
3040 BmInfo)))
3041 return (arc);
3042
3043 continue;
3044 }
3045
3046 // raise driver letter if OS/2 would recognize this drive
3047 if ( (MBoot.sPrtnInfo[i].bFileSysCode < PAR_PCIX)
3048 )
3049 fAssignLetter = TRUE;
3050
3051 if (fAssignLetter)
3052 (*pcLetter)++;
3053
3054 fBootable = ( (BmInfo)
3055 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
3056 );
3057
3058 if ((arc = AppendPartition(pppiFirst,
3059 pppiThis,
3060 posCount,
3061 PrDisk,
3062 (fBootable)
3063 ? (char*)&MBoot.sBmNames[i].name
3064 : "",
3065 (fAssignLetter)
3066 ? *pcLetter
3067 : ' ',
3068 MBoot.sPrtnInfo[i].bFileSysCode,
3069 FALSE, // primary
3070 fBootable, // bootable
3071 MBoot.sPrtnInfo[i].lTotalSects)))
3072 return (arc);
3073 }
3074 }
3075
3076 return (NO_ERROR);
3077}
3078
3079/*
3080 * GetExtendedPartition:
3081 * this finds the extended partition on the given
3082 * drive and calls GetLogicalDrives in turn.
3083 *
3084 * This gets called from doshGetPartitionsList.
3085 *
3086 * Based on code (C) Dmitry A. Steklenev.
3087 *
3088 *@@added V0.9.0 [umoeller]
3089 */
3090
3091APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
3092 PARTITIONINFO **pppiThis,
3093 PUSHORT posCount,
3094 PCHAR pcLetter,
3095 PAR_INFO* BmInfo,
3096 UINT iDisk) // in: disk to query
3097{
3098 APIRET arc = NO_ERROR;
3099 MBR_INFO MBoot; // Master Boot
3100 USHORT i;
3101
3102 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
3103 return (arc);
3104
3105 // go thru MBR entries to find extended partition
3106 for (i = 0;
3107 i < 4;
3108 i++)
3109 {
3110 if (MBoot.sPrtnInfo[i].bFileSysCode == PAR_EXTENDED)
3111 {
3112 if ((arc = GetLogicalDrives(pppiFirst,
3113 pppiThis,
3114 posCount,
3115 pcLetter,
3116 &MBoot.sPrtnInfo[i],
3117 iDisk,
3118 BmInfo)))
3119 return (arc);
3120 }
3121 }
3122
3123 return (NO_ERROR);
3124}
3125
3126/*
3127 *@@ CleanPartitionInfos:
3128 *
3129 *@@added V0.9.9 (2001-04-07) [umoeller]
3130 */
3131
3132VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
3133{
3134 while (ppiThis)
3135 {
3136 PPARTITIONINFO ppiNext = ppiThis->pNext;
3137 free(ppiThis);
3138 ppiThis = ppiNext;
3139 }
3140}
3141
3142/*
3143 *@@ doshGetPartitionsList:
3144 * this returns lots of information about the
3145 * partitions on all physical disks, which is
3146 * read directly from the MBRs and partition
3147 * tables.
3148 *
3149 * If NO_ERROR is returned by this function,
3150 * *ppPartitionInfo points to a linked list of
3151 * PARTITIONINFO structures, which has
3152 * *pusPartitionCount items.
3153 *
3154 * In that case, use doshFreePartitionsList to
3155 * free the resources allocated by this function.
3156 *
3157 * What this function returns depends on whether
3158 * LVM is installed.
3159 *
3160 * -- If LVM.DLL is found on the LIBPATH, this opens
3161 * the LVM engine and returns the info from the
3162 * LVM engine in the PARTITIONINFO structures.
3163 * The partitions are then sorted by disk in
3164 * ascending order.
3165 *
3166 * -- Otherwise, we parse the partition tables
3167 * manually. The linked list then starts out with
3168 * all the primary partitions, followed by the
3169 * logical drives in the extended partitions.
3170 * This function attempts to guess the correct drive
3171 * letters and stores these with the PARTITIONINFO
3172 * items, but there's no guarantee that this is
3173 * correct. We correctly ignore Linux partitions here
3174 * and give all primary partitions the C: letter, but
3175 * I have no idea what happens with NTFS partitions,
3176 * since I have none.
3177 *
3178 * If an error != NO_ERROR is returned, *pusContext
3179 * will be set to one of the following:
3180 *
3181 * -- 1: boot manager not found
3182 *
3183 * -- 2: primary partitions error
3184 *
3185 * -- 3: secondary partitions error
3186 *
3187 * -- 0: something else.
3188 *
3189 * Based on code (C) Dmitry A. Steklenev.
3190 *
3191 *@@added V0.9.0 [umoeller]
3192 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
3193 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
3194 */
3195
3196APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
3197 PUSHORT pusContext) // out: error context
3198{
3199 APIRET arc = NO_ERROR;
3200
3201 PLVMINFO pLVMInfo = NULL;
3202
3203 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
3204 *ppiTemp = NULL;
3205 USHORT cPartitions = 0; // bootable partition count
3206
3207 if (!ppList)
3208 return (ERROR_INVALID_PARAMETER);
3209
3210 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
3211 {
3212 // LVM installed:
3213 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
3214 &pPartitionInfos, // out: partitions array
3215 &cPartitions); // out: partitions count
3216 // copied to output below
3217
3218 if (arc)
3219 {
3220 // error: start over
3221 doshFreeLVMInfo(pLVMInfo);
3222 CleanPartitionInfos(pPartitionInfos);
3223 pPartitionInfos = NULL;
3224 cPartitions = 0;
3225 }
3226 }
3227
3228 if (arc)
3229 {
3230 // LVM not installed, or failed:
3231 // parse partitions manually
3232 PAR_INFO BmInfo; // BootManager partition
3233 USHORT usBmDisk; // BootManager disk
3234 USHORT cDisks = doshQueryDiskCount(); // physical disks count
3235 USHORT i;
3236
3237 CHAR cLetter = 'C'; // first drive letter
3238
3239 // start over
3240 arc = NO_ERROR;
3241
3242 if (cDisks > 8) // Not above 8 disks
3243 cDisks = 8;
3244
3245 // get boot manager disk and info
3246 if ((arc = doshGetBootManager(&usBmDisk,
3247 NULL,
3248 &BmInfo)) != NO_ERROR)
3249 {
3250 *pusContext = 1;
3251 }
3252 else
3253 {
3254 // on each disk, read primary partitions
3255 for (i = 1; i <= cDisks; i++)
3256 {
3257 if ((arc = GetPrimaryPartitions(&pPartitionInfos,
3258 &ppiTemp,
3259 &cPartitions,
3260 &cLetter,
3261 usBmDisk,
3262 usBmDisk ? &BmInfo : 0,
3263 i)))
3264 {
3265 *pusContext = 2;
3266 }
3267 }
3268
3269 if (!arc && usBmDisk)
3270 {
3271 // boot manager found:
3272 // on each disk, read extended partition
3273 // with logical drives
3274 for (i = 1; i <= cDisks; i++)
3275 {
3276 if ((arc = GetExtendedPartition(&pPartitionInfos,
3277 &ppiTemp,
3278 &cPartitions,
3279 &cLetter,
3280 &BmInfo,
3281 i)))
3282 {
3283 *pusContext = 3;
3284 }
3285 }
3286 }
3287 } // end else if ((arc = doshGetBootManager(&usBmDisk,
3288 } // end else if (!doshQueryLVMInfo(&pLVMInfo))
3289
3290 if (!arc)
3291 {
3292 // no error so far:
3293 *pusContext = 0;
3294
3295 *ppList = NEW(PARTITIONSLIST);
3296 if (!(*ppList))
3297 arc = ERROR_NOT_ENOUGH_MEMORY;
3298 else
3299 {
3300 ZERO(*ppList);
3301
3302 (*ppList)->pPartitionInfo = pPartitionInfos;
3303 (*ppList)->cPartitions = cPartitions;
3304
3305 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
3306 }
3307 }
3308
3309 if (arc)
3310 CleanPartitionInfos(pPartitionInfos);
3311
3312 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
3313
3314 return (arc);
3315}
3316
3317/*
3318 *@@ doshFreePartitionsList:
3319 * this frees the resources allocated by
3320 * doshGetPartitionsList.
3321 *
3322 *@@added V0.9.0 [umoeller]
3323 */
3324
3325APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
3326{
3327 if (!ppList)
3328 return (ERROR_INVALID_PARAMETER);
3329 else
3330 {
3331 CleanPartitionInfos(ppList->pPartitionInfo);
3332 doshFreeLVMInfo(ppList->pLVMInfo);
3333 free(ppList);
3334 }
3335
3336 return (NO_ERROR);
3337}
3338
3339/********************************************************************
3340 *
3341 * LVM declarations
3342 *
3343 ********************************************************************/
3344
3345/*
3346 *@@category: Helpers\Control program helpers\Partitions info\Quick LVM Interface
3347 * functions for transparently interfacing LVM.DLL.
3348 */
3349
3350typedef unsigned char BOOLEAN;
3351typedef unsigned short int CARDINAL16;
3352typedef unsigned long CARDINAL32;
3353typedef unsigned int CARDINAL;
3354typedef unsigned long DoubleWord;
3355
3356#ifdef ADDRESS
3357#undef ADDRESS
3358#endif
3359
3360typedef void* ADDRESS;
3361
3362#pragma pack(1)
3363
3364#define DISK_NAME_SIZE 20
3365#define FILESYSTEM_NAME_SIZE 20
3366#define PARTITION_NAME_SIZE 20
3367#define VOLUME_NAME_SIZE 20
3368
3369/*
3370 *@@ Drive_Control_Record:
3371 * invariant for a disk drive.
3372 *
3373 *@@added V0.9.9 (2001-04-07) [umoeller]
3374 */
3375
3376typedef struct _Drive_Control_Record
3377{
3378 CARDINAL32 Drive_Number; // OS/2 Drive Number for this drive.
3379 CARDINAL32 Drive_Size; // The total number of sectors on the drive.
3380 DoubleWord Drive_Serial_Number; // The serial number assigned to this drive. For info. purposes only.
3381 ADDRESS Drive_Handle; // Handle used for operations on the disk that this record corresponds to.
3382 CARDINAL32 Cylinder_Count; // The number of cylinders on the drive.
3383 CARDINAL32 Heads_Per_Cylinder; // The number of heads per cylinder for this drive.
3384 CARDINAL32 Sectors_Per_Track; // The number of sectors per track for this drive.
3385 BOOLEAN Drive_Is_PRM; // Set to TRUE if this drive is a PRM.
3386 BYTE Reserved[3]; // Alignment.
3387} Drive_Control_Record;
3388
3389/*
3390 *@@ Drive_Control_Array:
3391 * returned by the Get_Drive_Control_Data function
3392 *
3393 *@@added V0.9.9 (2001-04-07) [umoeller]
3394 */
3395
3396typedef struct _Drive_Control_Array
3397{
3398 Drive_Control_Record * Drive_Control_Data; // An array of drive control records.
3399 CARDINAL32 Count; // The number of entries in the array of drive control records.
3400} Drive_Control_Array;
3401
3402/*
3403 *@@ Drive_Information_Record:
3404 * defines the information that can be changed for a specific disk drive.
3405 *
3406 *@@added V0.9.9 (2001-04-07) [umoeller]
3407 */
3408
3409typedef struct _Drive_Information_Record
3410{
3411 CARDINAL32 Total_Available_Sectors; // The number of sectors on the disk which are not currently assigned to a partition.
3412 CARDINAL32 Largest_Free_Block_Of_Sectors; // The number of sectors in the largest contiguous block of available sectors.
3413 BOOLEAN Corrupt_Partition_Table; // If TRUE, then the partitioning information found on the drive is incorrect!
3414 BOOLEAN Unusable; // If TRUE, the drive's MBR is not accessible and the drive can not be partitioned.
3415 BOOLEAN IO_Error; // If TRUE, then the last I/O operation on this drive failed!
3416 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).
3417 char Drive_Name[DISK_NAME_SIZE]; // User assigned name for this disk drive.
3418} Drive_Information_Record;
3419
3420/*
3421 *@@ Partition_Information_Record:
3422 *
3423 *@@added V0.9.9 (2001-04-07) [umoeller]
3424 */
3425
3426typedef struct _Partition_Information_Record
3427{
3428 ADDRESS Partition_Handle;
3429 // The handle used to perform operations on this partition.
3430 ADDRESS Volume_Handle;
3431 // If this partition is part of a volume, this will be the handle of
3432 // the volume. If this partition is NOT part of a volume, then this
3433 // handle will be 0.
3434 ADDRESS Drive_Handle;
3435 // The handle for the drive this partition resides on.
3436 DoubleWord Partition_Serial_Number;
3437 // The serial number assigned to this partition.
3438 CARDINAL32 Partition_Start;
3439 // The LBA of the first sector of the partition.
3440 CARDINAL32 True_Partition_Size;
3441 // The total number of sectors comprising the partition.
3442 CARDINAL32 Usable_Partition_Size;
3443 // The size of the partition as reported to the IFSM. This is the
3444 // size of the partition less any LVM overhead.
3445 CARDINAL32 Boot_Limit;
3446 // The maximum number of sectors from this block of free space that
3447 // can be used to create a bootable partition if you allocate from the
3448 // beginning of the block of free space.
3449 BOOLEAN Spanned_Volume;
3450 // TRUE if this partition is part of a multi-partition volume.
3451 BOOLEAN Primary_Partition;
3452 // True or False. Any non-zero value here indicates that this partition
3453 // is a primary partition. Zero here indicates that this partition is
3454 // a "logical drive" - i.e. it resides inside of an extended partition.
3455 BYTE Active_Flag;
3456 // 80 = Partition is marked as being active.
3457 // 0 = Partition is not active.
3458 BYTE OS_Flag;
3459 // This field is from the partition table. It is known as the OS flag,
3460 // the Partition Type Field, Filesystem Type, and various other names.
3461 // Values of interest
3462 // If this field is: (values are in hex)
3463 // 07 = The partition is a compatibility partition formatted for use
3464 // with an installable filesystem, such as HPFS or JFS.
3465 // 00 = Unformatted partition
3466 // 01 = FAT12 filesystem is in use on this partition.
3467 // 04 = FAT16 filesystem is in use on this partition.
3468 // 0A = OS/2 Boot Manager Partition
3469 // 35 = LVM partition
3470 // 84 = OS/2 FAT16 partition which has been relabeled by Boot Manager to "Hide" it.
3471 BYTE Partition_Type;
3472 // 0 = Free Space
3473 // 1 = LVM Partition (Part of an LVM Volume.)
3474 // 2 = Compatibility Partition
3475 // All other values are reserved for future use.
3476 BYTE Partition_Status;
3477 // 0 = Free Space
3478 // 1 = In Use - i.e. already assigned to a volume.
3479 // 2 = Available - i.e. not currently assigned to a volume.
3480 BOOLEAN On_Boot_Manager_Menu;
3481 // Set to TRUE if this partition is not part of a Volume yet is on the
3482 // Boot Manager Menu.
3483 BYTE Reserved;
3484 // Alignment.
3485 char Volume_Drive_Letter;
3486 // The drive letter assigned to the volume that this partition is a part of.
3487 char Drive_Name[DISK_NAME_SIZE];
3488 // User assigned name for this disk drive.
3489 char File_System_Name[FILESYSTEM_NAME_SIZE];
3490 // The name of the filesystem in use on this partition, if it is known.
3491 char Partition_Name[PARTITION_NAME_SIZE];
3492 // The user assigned name for this partition.
3493 char Volume_Name[VOLUME_NAME_SIZE];
3494 // If this partition is part of a volume, then this will be the
3495 // name of the volume that this partition is a part of. If this
3496 // record represents free space, then the Volume_Name will be
3497 // "FREE SPACE xx", where xx is a unique numeric ID generated by
3498 // LVM.DLL. Otherwise it will be an empty string.
3499} Partition_Information_Record;
3500
3501// The following defines are for use with the Partition_Type field in the
3502// Partition_Information_Record.
3503#define FREE_SPACE_PARTITION 0
3504#define LVM_PARTITION 1
3505#define COMPATIBILITY_PARTITION 2
3506
3507// The following defines are for use with the Partition_Status field in the
3508// Partition_Information_Record.
3509#define PARTITION_IS_IN_USE 1
3510#define PARTITION_IS_AVAILABLE 2
3511#define PARTITION_IS_FREE_SPACE 0
3512
3513/*
3514 *@@ Partition_Information_Array:
3515 * returned by various functions in the LVM Engine.
3516 *
3517 *@@added V0.9.9 (2001-04-07) [umoeller]
3518 */
3519
3520typedef struct _Partition_Information_Array
3521{
3522 Partition_Information_Record * Partition_Array; // An array of Partition_Information_Records.
3523 CARDINAL32 Count; // The number of entries in the Partition_Array.
3524} Partition_Information_Array;
3525
3526/*
3527 *@@ Volume_Information_Record:
3528 * variable information for a volume.
3529 *
3530 *@@added V0.9.9 (2001-04-07) [umoeller]
3531 */
3532
3533typedef struct _Volume_Information_Record
3534{
3535 CARDINAL32 Volume_Size;
3536 // The number of sectors comprising the volume.
3537 CARDINAL32 Partition_Count;
3538 // The number of partitions which comprise this volume.
3539 CARDINAL32 Drive_Letter_Conflict;
3540 // 0 indicates that the drive letter preference for this volume is unique.
3541 // 1 indicates that the drive letter preference for this volume
3542 // is not unique, but this volume got its preferred drive letter anyway.
3543 // 2 indicates that the drive letter preference for this volume
3544 // is not unique, and this volume did NOT get its preferred drive letter.
3545 // 4 indicates that this volume is currently "hidden" - i.e. it has
3546 // no drive letter preference at the current time.
3547 BOOLEAN Compatibility_Volume;
3548 // TRUE if this is for a compatibility volume, FALSE otherwise.
3549 BOOLEAN Bootable;
3550 // Set to TRUE if this volume appears on the Boot Manager menu, or if it is
3551 // a compatibility volume and its corresponding partition is the first active
3552 // primary partition on the first drive.
3553 char Drive_Letter_Preference;
3554 // The drive letter that this volume desires to be.
3555 char Current_Drive_Letter;
3556 // The drive letter currently used to access this volume.
3557 // May be different than Drive_Letter_Preference if there was a conflict ( i.e. Drive_Letter_Preference
3558 // is already in use by another volume ).
3559 char Initial_Drive_Letter;
3560 // The drive letter assigned to this volume by the operating system
3561 // when LVM was started. This may be different from the
3562 // Drive_Letter_Preference if there were conflicts, and
3563 // may be different from the Current_Drive_Letter. This
3564 // will be 0x0 if the Volume did not exist when the LVM Engine
3565 // was opened (i.e. it was created during this LVM session).
3566 BOOLEAN New_Volume;
3567 // Set to FALSE if this volume existed before the LVM Engine was
3568 // opened. Set to TRUE if this volume was created after the LVM
3569 // Engine was opened.
3570 BYTE Status;
3571 // 0 = None.
3572 // 1 = Bootable
3573 // 2 = Startable
3574 // 3 = Installable.
3575 BYTE Reserved_1;
3576 char Volume_Name[VOLUME_NAME_SIZE];
3577 // The user assigned name for this volume.
3578 char File_System_Name[FILESYSTEM_NAME_SIZE];
3579 // The name of the filesystem in use on this partition, if it
3580 // is known.
3581} Volume_Information_Record;
3582
3583#pragma pack()
3584
3585/********************************************************************
3586 *
3587 * Quick LVM Interface API
3588 *
3589 ********************************************************************/
3590
3591/*
3592 *@@ LVMINFOPRIVATE:
3593 * private structure used by doshQueryLVMInfo.
3594 * This is what the LVMINFO pointer really
3595 * points to.
3596 *
3597 *@@added V0.9.9 (2001-04-07) [umoeller]
3598 */
3599
3600typedef struct _LVMINFOPRIVATE
3601{
3602 LVMINFO LVMInfo; // public structure (dosh.h)
3603
3604 // function pointers resolved from LVM.DLL
3605
3606 void (* _System Open_LVM_Engine)(BOOLEAN Ignore_CHS,
3607 CARDINAL32 *Error_Code);
3608
3609 void (* _System Free_Engine_Memory)(ADDRESS Object);
3610
3611 void (* _System Close_LVM_Engine)(void);
3612
3613 Drive_Control_Array (* _System
3614 Get_Drive_Control_Data)(CARDINAL32 *Error_Code);
3615
3616 Drive_Information_Record (* _System
3617 Get_Drive_Status)(ADDRESS Drive_Handle,
3618 CARDINAL32 *Error_Code);
3619
3620 Partition_Information_Array (* _System
3621 Get_Partitions)(ADDRESS Handle,
3622 CARDINAL32 *Error_Code);
3623
3624 Volume_Information_Record (*_System
3625 Get_Volume_Information)(ADDRESS Volume_Handle,
3626 CARDINAL32 *Error_Code);
3627
3628} LVMINFOPRIVATE, *PLVMINFOPRIVATE;
3629
3630#define LVM_ERROR_FIRST 20000
3631
3632/*
3633 *@@ doshQueryLVMInfo:
3634 * creates an LVMINFO structure if LVM is installed.
3635 * Returns that structure (which the caller must free
3636 * using doshFreeLVMInfo) or NULL if LVM.DLL was not
3637 * found along the LIBPATH.
3638 *
3639 *@@added V0.9.9 (2001-04-07) [umoeller]
3640 */
3641
3642APIRET doshQueryLVMInfo(PLVMINFO *ppLVMInfo)
3643{
3644 APIRET arc = NO_ERROR;
3645 CHAR szError[100];
3646 PLVMINFOPRIVATE pLVMInfo = NULL;
3647 HMODULE hmodLVM = NULLHANDLE;
3648
3649 if (!(arc = DosLoadModule(szError,
3650 sizeof(szError),
3651 "LVM",
3652 &hmodLVM)))
3653 {
3654 // got LVM.DLL:
3655 pLVMInfo = NEW(LVMINFOPRIVATE);
3656 if (!pLVMInfo)
3657 arc = ERROR_NOT_ENOUGH_MEMORY;
3658 else
3659 {
3660 // array of function pointers to be resolved from LVM.DLL
3661 RESOLVEFUNCTION aFunctions[] =
3662 {
3663 "Open_LVM_Engine", (PFN*)&pLVMInfo->Open_LVM_Engine,
3664 "Free_Engine_Memory", (PFN*)&pLVMInfo->Free_Engine_Memory,
3665 "Close_LVM_Engine", (PFN*)&pLVMInfo->Close_LVM_Engine,
3666 "Get_Drive_Control_Data", (PFN*)&pLVMInfo->Get_Drive_Control_Data,
3667 "Get_Drive_Status", (PFN*)&pLVMInfo->Get_Drive_Status,
3668 "Get_Partitions", (PFN*)&pLVMInfo->Get_Partitions,
3669 "Get_Volume_Information", (PFN*)&pLVMInfo->Get_Volume_Information
3670 };
3671 ULONG ul;
3672
3673 ZERO(pLVMInfo);
3674
3675 pLVMInfo->LVMInfo.hmodLVM = hmodLVM;
3676
3677 // now resolve function pointers
3678 for (ul = 0;
3679 ul < ARRAYITEMCOUNT(aFunctions);
3680 ul++)
3681 {
3682 PRESOLVEFUNCTION pFuncThis = &aFunctions[ul];
3683 arc = DosQueryProcAddr(hmodLVM,
3684 0, // ordinal, ignored
3685 (PSZ)pFuncThis->pcszFunctionName,
3686 pFuncThis->ppFuncAddress);
3687 if (!pFuncThis->ppFuncAddress)
3688 arc = ERROR_INVALID_NAME;
3689
3690 if (arc)
3691 break;
3692 }
3693 }
3694 }
3695
3696 if (arc)
3697 doshFreeLVMInfo((PLVMINFO)pLVMInfo);
3698 else
3699 *ppLVMInfo = (PLVMINFO)pLVMInfo;
3700
3701 return (arc);
3702}
3703
3704/*
3705 *@@ doshReadLVMPartitions:
3706 * using the LVMINFO parameter from doshQueryLVMInfo,
3707 * builds an array of PARTITIONINFO structures with
3708 * the data returned from LVM.DLL.
3709 *
3710 *@@added V0.9.9 (2001-04-07) [umoeller]
3711 */
3712
3713APIRET doshReadLVMPartitions(PLVMINFO pInfo, // in: LVM info
3714 PPARTITIONINFO *ppPartitionInfo, // out: partitions array
3715 PUSHORT pcPartitions) // out: partitions count
3716{
3717 APIRET arc = NO_ERROR;
3718 CARDINAL32 Error = 0;
3719
3720 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
3721 *ppiTemp = NULL;
3722 USHORT cPartitions = 0; // bootable partition count
3723 PLVMINFOPRIVATE pLVMInfo = (PLVMINFOPRIVATE)pInfo;
3724
3725 _Pmpf((__FUNCTION__ ": entering"));
3726
3727 if (!pLVMInfo)
3728 return (ERROR_INVALID_PARAMETER);
3729
3730 // initialize LVM engine
3731 pLVMInfo->Open_LVM_Engine(TRUE,
3732 &Error);
3733
3734 _Pmpf((" Open_LVM_Engine Error: %d"));
3735
3736 if (!Error)
3737 {
3738 Drive_Control_Array DCA = pLVMInfo->Get_Drive_Control_Data(&Error);
3739 // member records to be freed
3740
3741 _Pmpf((" Get_Drive_Control_Data Error: %d, drive count: %d", Error, DCA.Count));
3742
3743 if ( (!Error)
3744 && (DCA.Count)
3745 )
3746 {
3747 // DCA.Drive_Control_Data now contains drive information records;
3748 // this must be freed
3749 ULONG ulDisk;
3750
3751 for (ulDisk = 0;
3752 ulDisk < DCA.Count;
3753 ulDisk++)
3754 {
3755 Drive_Control_Record *pDriveControlRecord
3756 = &DCA.Drive_Control_Data[ulDisk];
3757 ADDRESS hDrive = pDriveControlRecord->Drive_Handle;
3758
3759 Drive_Information_Record pDriveInfoRecord
3760 = pLVMInfo->Get_Drive_Status(hDrive,
3761 &Error);
3762
3763 _Pmpf((" drive %d Get_Drive_Status Error: %d", ulDisk, Error));
3764
3765 if (!Error)
3766 {
3767 Partition_Information_Array PIA
3768 = pLVMInfo->Get_Partitions(hDrive,
3769 &Error);
3770
3771 _Pmpf((" Get_Partitions Error: %d", Error));
3772
3773 if (!Error)
3774 {
3775 // PIA.Partition_Array now contains
3776 // Partition_Information_Record; must be freed
3777
3778 // now go thru partitions of this drive
3779 ULONG ulPart;
3780 for (ulPart = 0;
3781 ulPart < PIA.Count;
3782 ulPart++)
3783 {
3784 Partition_Information_Record *pPartition
3785 = &PIA.Partition_Array[ulPart];
3786 Volume_Information_Record VolumeInfo;
3787
3788 const char *pcszBootName = NULL; // for now
3789 BOOL fBootable = FALSE;
3790
3791 if (pPartition->Volume_Handle)
3792 {
3793 // this partition is part of a volume:
3794 // only then can it be bootable...
3795 // get the volume info
3796 VolumeInfo
3797 = pLVMInfo->Get_Volume_Information(pPartition->Volume_Handle,
3798 &Error);
3799 pcszBootName = VolumeInfo.Volume_Name;
3800
3801 fBootable = (VolumeInfo.Status == 1);
3802 }
3803
3804
3805 if (arc = AppendPartition(&pPartitionInfos,
3806 &ppiTemp,
3807 &cPartitions,
3808 ulDisk + 1,
3809 pcszBootName,
3810 pPartition->Volume_Drive_Letter,
3811 pPartition->OS_Flag, // FS type
3812 pPartition->Primary_Partition,
3813 fBootable,
3814 pPartition->True_Partition_Size))
3815 break;
3816 }
3817
3818 // clean up partitions
3819 pLVMInfo->Free_Engine_Memory(PIA.Partition_Array);
3820 }
3821 }
3822 else
3823 // error:
3824 break;
3825 }
3826
3827 // clean up drive data
3828 pLVMInfo->Free_Engine_Memory(DCA.Drive_Control_Data);
3829 }
3830 }
3831
3832 // close LVM
3833 pLVMInfo->Close_LVM_Engine();
3834
3835 if (Error)
3836 {
3837 // if we got an error, return it with the
3838 // LVM error offset
3839 arc = LVM_ERROR_FIRST + Error;
3840
3841 CleanPartitionInfos(pPartitionInfos);
3842 }
3843
3844 if (!arc)
3845 {
3846 *ppPartitionInfo = pPartitionInfos;
3847 *pcPartitions = cPartitions;
3848 }
3849
3850 _Pmpf((__FUNCTION__ ": exiting, arg = %d", arc));
3851
3852 return (arc);
3853}
3854
3855/*
3856 *@@ doshFreeLVMInfo:
3857 *
3858 *@@added V0.9.9 (2001-04-07) [umoeller]
3859 */
3860
3861VOID doshFreeLVMInfo(PLVMINFO pInfo)
3862{
3863 if (pInfo)
3864 {
3865 if (pInfo->hmodLVM)
3866 DosFreeModule(pInfo->hmodLVM);
3867
3868 free(pInfo);
3869 }
3870}
Note: See TracBrowser for help on using the repository browser.