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

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

Lots of updates from the last week for conditional compiles and other stuff.

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