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

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

Misc updates.

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