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

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

Lots of changes for icons and refresh.

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