source: branches/branch-1-0/src/helpers/dosh2.c@ 368

Last change on this file since 368 was 364, checked in by pr, 17 years ago

Add doshIsFixpak for bug 1067.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 84.1 KB
Line 
1
2/*
3 *@@sourcefile dosh2.c:
4 * dosh.c contains more Control Program helper functions.
5 *
6 * This file is new with V0.9.4 (2000-07-26) [umoeller].
7 *
8 * As opposed to the functions in dosh.c, these require
9 * linking against other helpers. As a result, these have
10 * been separated from dosh.c to allow linking against
11 * dosh.obj only.
12 *
13 * Function prefixes:
14 * -- dosh* Dos (Control Program) helper functions
15 *
16 * This has the same header as dosh.c, dosh.h.
17 *
18 * The partition functions in this file are based on
19 * code which has kindly been provided by Dmitry A. Steklenev.
20 * See doshGetPartitionsList for how to use these.
21 *
22 * Note: Version numbering in this file relates to XWorkplace version
23 * numbering.
24 *
25 *@@header "helpers\dosh.h"
26 *@@added V0.9.4 (2000-07-27) [umoeller]
27 */
28
29/*
30 * This file Copyright (C) 1997-2008 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\level.h"
68#include "helpers\nls.h"
69#include "helpers\standards.h"
70#include "helpers\stringh.h"
71
72#pragma hdrstop
73
74/*
75 *@@category: Helpers\Control program helpers\Miscellaneous
76 */
77
78/* ******************************************************************
79 *
80 * Miscellaneous
81 *
82 ********************************************************************/
83
84/*
85 *@@ doshIsWarp4:
86 * checks the OS/2 system version number.
87 *
88 * Returns:
89 *
90 * -- 0 (FALSE): OS/2 2.x or Warp 3 is running.
91 *
92 * -- 1: Warp 4.0 is running.
93 *
94 * -- 2: Warp 4.5 kernel is running on Warp 4.0 (Warp 4 FP 13+).
95 *
96 * -- 3: Warp 4.5 is running (WSeB or eCS or ACP/MCP), or even something newer.
97 *
98 *@@changed V0.9.2 (2000-03-05) [umoeller]: reported TRUE on Warp 3 also; fixed
99 *@@changed V0.9.6 (2000-10-16) [umoeller]: patched for speed
100 *@@changed V0.9.9 (2001-04-04) [umoeller]: now returning 2 for Warp 4.5 and above
101 *@@changed V1.0.5 (2006-05-29) [pr]: now returning 3 for Warp 4.5 and above and 2 for
102 * Warp 4.0 FP13+; moved here from dosh.c
103 */
104
105ULONG doshIsWarp4(VOID)
106{
107 static BOOL s_fQueried = FALSE;
108 static ULONG s_ulrc = 0;
109
110 if (!s_fQueried)
111 {
112 // first call:
113 ULONG aulBuf[3];
114
115 DosQuerySysInfo(QSV_VERSION_MAJOR, // 11
116 QSV_VERSION_MINOR, // 12
117 &aulBuf, sizeof(aulBuf));
118 // Warp 3 is reported as 20.30
119 // Warp 4 is reported as 20.40
120 // Aurora is reported as 20.45 (regardless of convenience packs)
121
122 if (aulBuf[0] > 20) // major > 20; not the case with Warp 3, 4, 5
123 s_ulrc = 3;
124 else
125 if (aulBuf[0] == 20) // major == 20
126 if (aulBuf[1] >= 45) // minor >= 45 Warp 4 FP13 or later
127 s_ulrc = 2;
128 else
129 if (aulBuf[1] == 40) // minor == 40 Warp 4 pre-FP13
130 s_ulrc = 1;
131
132 // Now check SYSLEVEL.OS2 to detect between Warp 4 and MCP
133 if (s_ulrc == 2)
134 {
135 CHAR szName[CCHMAXPATH] = "?:\\OS2\\INSTALL\\SYSLEVEL.OS2";
136 ULONG cbFile;
137 PXFILE pFile;
138
139 szName[0] = doshQueryBootDrive();
140 if (!doshOpen(szName,
141 XOPEN_READ_EXISTING | XOPEN_BINARY,
142 &cbFile,
143 &pFile))
144 {
145 CHAR szVersion[2];
146 ULONG ulSize;
147
148 if ( !lvlQueryLevelFileData(pFile->hf,
149 QLD_MINORVERSION,
150 szVersion,
151 sizeof(szVersion),
152 &ulSize)
153 && (szVersion[0] >= '5')) // minor >= 5 is MCP
154 s_ulrc = 3;
155
156 doshClose(&pFile);
157 }
158 }
159
160 s_fQueried = TRUE;
161 }
162
163 return (s_ulrc);
164}
165
166/*
167 *@@ doshIsFixpak:
168 * checks if the OS/2 system is at a given fixpak level or higher
169 *
170 * Returns:
171 *
172 * -- FALSE: the system is at a lower level
173 *
174 * -- TRUE: the system is at the exact or a higher level
175 *
176 *@@added V1.0.8 (2008-04-07) [chennecke]: @@fixes 1067
177 */
178
179BOOL doshIsFixpak(BOOL fIsMcp, // in: MCP fixpak?
180 ULONG ulFixpakLevel) // in: fixpak level number
181{
182 static BOOL s_fQueried = FALSE;
183 static BOOL s_fRc = FALSE;
184
185 if (!s_fQueried)
186 {
187 // first call:
188 // check SYSLEVEL.OS2
189 CHAR szName[CCHMAXPATH] = "?:\\OS2\\INSTALL\\SYSLEVEL.OS2";
190 ULONG cbFile;
191 PXFILE pFile;
192
193 szName[0] = doshQueryBootDrive();
194 if (!doshOpen(szName,
195 XOPEN_READ_EXISTING | XOPEN_BINARY,
196 &cbFile,
197 &pFile))
198 {
199 CHAR szCsdLevel[8];
200 CHAR szCsdLevelNumber[4];
201 ULONG ulSize;
202
203 if (!lvlQueryLevelFileData(pFile->hf,
204 QLD_CURRENTCSD,
205 szCsdLevel,
206 sizeof(szCsdLevel),
207 &ulSize))
208 {
209 strncpy(szCsdLevelNumber, szCsdLevel + 4, 3);
210 szCsdLevelNumber[sizeof(szCsdLevelNumber) - 1] = '\0';
211 if (fIsMcp)
212 {
213 if ( (szCsdLevel[3] == 'C')
214 && (atol(szCsdLevelNumber) >= ulFixpakLevel))
215 s_fRc = TRUE;
216 }
217 else
218 {
219 if ( (szCsdLevel[3] == 'C')
220 || ( (szCsdLevel[3] == 'M')
221 && (atol(szCsdLevelNumber) >= ulFixpakLevel)))
222 s_fRc = TRUE;
223 }
224 }
225
226 doshClose(&pFile);
227 }
228
229 s_fQueried = TRUE;
230 }
231
232 return (s_fRc);
233}
234
235
236/*
237 *@@ doshIsValidFileName:
238 * this returns NO_ERROR only if pszFile is a valid file name.
239 * This may include a full path.
240 *
241 * If a drive letter is specified, this checks for whether
242 * that drive is a FAT drive and adjust the checks accordingly,
243 * i.e. 8+3 syntax (per path component).
244 *
245 * If no drive letter is specified, this check is performed
246 * for the current drive.
247 *
248 * This also checks if pszFileNames contains characters which
249 * are invalid for the current drive.
250 *
251 * Note: this performs syntactic checks only. This does not
252 * check for whether the specified path components exist.
253 * However, it _is_ checked for whether the given drive
254 * exists.
255 *
256 * This func is especially useful to check filenames that
257 * have been entered by the user in a "Save as" dialog.
258 *
259 * If an error is found, the corresponding DOS error code
260 * is returned:
261 * -- ERROR_INVALID_DRIVE
262 * -- ERROR_FILENAME_EXCED_RANGE (on FAT: no 8+3 filename)
263 * -- ERROR_INVALID_NAME (invalid character)
264 * -- ERROR_CURRENT_DIRECTORY (if fFullyQualified: no full path specified)
265 *
266 *@@changed V0.9.2 (2000-03-11) [umoeller]: added fFullyQualified
267 */
268
269APIRET doshIsValidFileName(const char* pcszFile,
270 BOOL fFullyQualified) // in: if TRUE, pcszFile must be fully q'fied
271{
272 APIRET arc = NO_ERROR;
273 CHAR szPath[CCHMAXPATH+4] = " :";
274 CHAR szComponent[CCHMAXPATH];
275 PSZ p1, p2;
276 BOOL fIsFAT = FALSE;
277 PSZ pszInvalid;
278
279 if (fFullyQualified) // V0.9.2 (2000-03-11) [umoeller]
280 {
281 if ( (*(pcszFile + 1) != ':')
282 || (*(pcszFile + 2) != '\\')
283 )
284 arc = ERROR_CURRENT_DIRECTORY;
285 }
286
287 // check drive first
288 if (*(pcszFile + 1) == ':')
289 {
290 CHAR cDrive = toupper(*pcszFile);
291 double d;
292 // drive specified:
293 strcpy(szPath, pcszFile);
294 szPath[0] = toupper(*pcszFile);
295 arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
296 }
297 else
298 {
299 // no drive specified: take current
300 ULONG ulDriveNum = 0,
301 ulDriveMap = 0;
302 arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
303 szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
304 szPath[1] = ':';
305 strcpy(&szPath[2], pcszFile);
306 }
307
308 if (arc == NO_ERROR)
309 {
310 fIsFAT = doshIsFileOnFAT(szPath);
311
312 pszInvalid = (fIsFAT)
313 ? "<>|+=:;,\"/[] " // invalid characters in FAT
314 : "<>|:\"/"; // invalid characters in IFS's
315
316 // now separate path components
317 p1 = &szPath[2]; // advance past ':'
318
319 do {
320
321 if (*p1 == '\\')
322 p1++;
323
324 p2 = strchr(p1, '\\');
325 if (p2 == NULL)
326 p2 = p1 + strlen(p1);
327
328 if (p1 != p2)
329 {
330 LONG lDotOfs = -1,
331 lAfterDot = -1;
332 ULONG cbFile,
333 ul;
334 PSZ pSource = szComponent;
335
336 strncpy(szComponent, p1, p2-p1);
337 szComponent[p2-p1] = 0;
338 cbFile = strlen(szComponent);
339
340 // now check each path component
341 for (ul = 0; ul < cbFile; ul++)
342 {
343 if (fIsFAT)
344 {
345 // on FAT: only 8 characters allowed before dot
346 if (*pSource == '.')
347 {
348 lDotOfs = ul;
349 lAfterDot = 0;
350 if (ul > 7)
351 return (ERROR_FILENAME_EXCED_RANGE);
352 }
353 }
354 // and check for invalid characters
355 if (strchr(pszInvalid, *pSource) != NULL)
356 return (ERROR_INVALID_NAME);
357
358 pSource++;
359
360 // on FAT, allow only three chars after dot
361 if (fIsFAT)
362 if (lAfterDot != -1)
363 {
364 lAfterDot++;
365 if (lAfterDot > 3)
366 return (ERROR_FILENAME_EXCED_RANGE);
367 }
368 }
369
370 // we are still missing the case of a FAT file
371 // name without extension; if so, check whether
372 // the file stem is <= 8 chars
373 if (fIsFAT)
374 if (lDotOfs == -1) // dot not found:
375 if (cbFile > 8)
376 return (ERROR_FILENAME_EXCED_RANGE);
377 }
378
379 // go for next component
380 p1 = p2+1;
381 } while (*p2);
382 }
383
384 return arc;
385}
386
387/*
388 *@@ doshMakeRealName:
389 * this copies pszSource to pszTarget, replacing
390 * all characters which are not supported by file
391 * systems with cReplace.
392 *
393 * pszTarget must be at least the same size as pszSource.
394 * If (fIsFAT), the file name will be made FAT-compliant (8+3).
395 *
396 * Returns TRUE if characters were replaced.
397 *
398 *@@changed V0.9.0 (99-11-06) [umoeller]: now replacing "*" too
399 */
400
401BOOL doshMakeRealName(PSZ pszTarget, // out: new real name
402 PSZ pszSource, // in: filename to translate
403 CHAR cReplace, // in: replacement char for invalid
404 // characters (e.g. '!')
405 BOOL fIsFAT) // in: make-FAT-compatible flag
406{
407 ULONG ul,
408 cbSource = strlen(pszSource);
409 LONG lDotOfs = -1,
410 lAfterDot = -1;
411 BOOL brc = FALSE;
412 PSZ pSource = pszSource,
413 pTarget = pszTarget;
414
415 const char *pcszInvalid = (fIsFAT)
416 ? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
417 : "*<>|:\"/\\"; // invalid characters in IFS's
418
419 for (ul = 0; ul < cbSource; ul++)
420 {
421 if (fIsFAT)
422 {
423 // on FAT: truncate filename if neccessary
424 if (*pSource == '.')
425 {
426 lDotOfs = ul;
427 lAfterDot = 0;
428 if (ul > 7) {
429 // only 8 characters allowed before dot,
430 // so set target ptr to dot pos
431 pTarget = pszTarget+8;
432 }
433 }
434 }
435 // and replace invalid characters
436 if (strchr(pcszInvalid, *pSource) == NULL)
437 *pTarget = *pSource;
438 else
439 {
440 *pTarget = cReplace;
441 brc = TRUE;
442 }
443 pTarget++;
444 pSource++;
445
446 // on FAT, allow only three chars after dot
447 if (fIsFAT)
448 if (lAfterDot != -1)
449 {
450 lAfterDot++;
451 if (lAfterDot > 3)
452 break;
453 }
454 }
455 *pTarget = '\0';
456
457 if (fIsFAT)
458 {
459 // we are still missing the case of a FAT file
460 // name without extension; if so, check whether
461 // the file stem is <= 8 chars
462 if (lDotOfs == -1) // dot not found:
463 if (cbSource > 8)
464 *(pszTarget+8) = 0; // truncate
465
466 // convert to upper case
467 strupr(pszTarget);
468 }
469
470 return brc;
471}
472
473/*
474 *@@ doshSetCurrentDir:
475 * sets the current working directory
476 * to the given path.
477 *
478 * As opposed to DosSetCurrentDir, this
479 * one will change the current drive
480 * also, if one is specified.
481 *
482 *@@changed V0.9.9 (2001-04-04) [umoeller]: this returned an error even if none occurred, fixed
483 */
484
485APIRET doshSetCurrentDir(const char *pcszDir)
486{
487 APIRET arc = NO_ERROR;
488 if (!pcszDir)
489 return (ERROR_INVALID_PARAMETER);
490 {
491 if (*pcszDir != 0)
492 if (*(pcszDir+1) == ':')
493 {
494 // drive given:
495 CHAR cDrive = toupper(*(pcszDir));
496 // change drive
497 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
498 // 1 = A:, 2 = B:, ...
499 }
500
501 arc = DosSetCurrentDir((PSZ)pcszDir);
502 }
503
504 return arc; // V0.9.9 (2001-04-04) [umoeller]
505}
506
507/*
508 *@@ CopyToBuffer:
509 * little helper for copying a string to
510 * a target buffer with length checking.
511 *
512 * Returns:
513 *
514 * -- NO_ERROR
515 *
516 * -- ERROR_BUFFER_OVERFLOW if pszTarget does
517 * not have enough room to hold pcszSource
518 * (including the null terminator).
519 *
520 *@@added V0.9.16 (2001-10-08) [umoeller]
521 */
522
523STATIC APIRET CopyToBuffer(PSZ pszTarget, // out: target buffer
524 PCSZ pcszSource, // in: source string
525 ULONG cbTarget) // in: size of target buffer
526{
527 ULONG ulLength = strlen(pcszSource);
528 if (ulLength < cbTarget)
529 {
530 memcpy(pszTarget,
531 pcszSource,
532 ulLength + 1);
533 return NO_ERROR;
534 }
535
536 return(ERROR_BUFFER_OVERFLOW);
537}
538
539/*
540 *@@ doshSearchPath:
541 * replacement for DosSearchPath.
542 *
543 * This looks along all directories which are
544 * specified in the value of the given environment
545 * variable if pcszFile is found.
546 *
547 * As opposed to the stupid DosSearchPath, this
548 * ignores subdirectories in the path particles.
549 * For example, DosSearchPath would usually not
550 * find an INSTALL file because \OS2 contains
551 * an INSTALL directory, or NETSCAPE because
552 * \OS2\INSTALL contains a NETSCAPE directory.
553 *
554 * Returns:
555 *
556 * -- NO_ERROR: pszExecutable has received the
557 * full path of pcszFile.
558 *
559 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
560 * in the specified path (or is a directory).
561 *
562 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
563 * the pszExecutable buffer is too small to hold
564 * the full path.
565 *
566 *@@added V0.9.16 (2001-10-08) [umoeller]
567 *@@changed V1.0.0 (2002-11-23) [umoeller]: allowing NULL pcszPath to search for "PATH"
568 */
569
570APIRET doshSearchPath(const char *pcszPath, // in: path variable name; if NULL, we use "PATH"
571 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
572 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
573 ULONG cbExecutable) // in: sizeof (*pszExecutable)
574{
575 APIRET arc = NO_ERROR;
576
577 // get the PATH value
578 PCSZ pcszPathValue;
579
580 if (!pcszPath)
581 pcszPath = "PATH"; // V1.0.0 (2002-11-23) [umoeller]
582
583 if (!(arc = DosScanEnv((PSZ)pcszPath,
584#if __cplusplus
585 &pcszPathValue)))
586#else
587 (PSZ*)&pcszPathValue)))
588#endif
589 {
590 // run thru the path components
591 PSZ pszPathCopy;
592 if (pszPathCopy = strdup(pcszPathValue))
593 {
594 PSZ pszToken = strtok(pszPathCopy, ";");
595 while (pszToken)
596 {
597 CHAR szFileMask[2*CCHMAXPATH];
598 FILESTATUS3 fs3;
599
600 sprintf(szFileMask,
601 "%s\\%s",
602 pszToken, // path particle
603 pcszFile); // e.g. "netscape"
604
605 if ( (!(arc = DosQueryPathInfo(szFileMask,
606 FIL_STANDARD,
607 &fs3,
608 sizeof(fs3))))
609 // make sure it's not a directory
610 // and that it's not hidden
611 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
612 )
613 {
614 // copy
615 arc = CopyToBuffer(pszExecutable,
616 szFileMask,
617 cbExecutable);
618 // and stop
619 break;
620 }
621 else
622 arc = ERROR_FILE_NOT_FOUND;
623 // and search on
624
625 pszToken = strtok(NULL, ";");
626 };
627
628 free(pszPathCopy);
629 }
630 else
631 arc = ERROR_NOT_ENOUGH_MEMORY;
632 }
633
634 return arc;
635}
636
637/*
638 *@@ doshSearchDirs:
639 * This looks along all directories which are
640 * specified in the passed directory list
641 * if pcszFile is found.
642 *
643 * As opposed to the stupid DosSearchPath, this
644 * ignores subdirectories in the path particles.
645 * For example, DosSearchPath would usually not
646 * find an INSTALL file because \OS2 contains
647 * an INSTALL directory, or NETSCAPE because
648 * \OS2\INSTALL contains a NETSCAPE directory.
649 *
650 * Returns:
651 *
652 * -- NO_ERROR: pszExecutable has received the
653 * full path of pcszFile.
654 *
655 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
656 * in the specified path (or is a directory).
657 *
658 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
659 * the pszExecutable buffer is too small to hold
660 * the full path.
661 *
662 *@@added V1.0.4 (2005-06-16) [chennecke]: blatantly stolen from doshSearchPath
663 */
664
665APIRET doshSearchDirs(const char *pcszDirList, // in: list of directories in PATH style
666 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
667 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
668 ULONG cbExecutable) // in: sizeof (*pszExecutable)
669{
670 APIRET arc = NO_ERROR;
671
672 // run thru the path components
673 PSZ pszPathCopy;
674 if (pszPathCopy = strdup(pcszDirList))
675 {
676 PSZ pszToken = strtok(pszPathCopy, ";");
677 while (pszToken)
678 {
679 CHAR szFileMask[2*CCHMAXPATH];
680 FILESTATUS3 fs3;
681
682 sprintf(szFileMask,
683 "%s\\%s",
684 pszToken, // path particle
685 pcszFile); // e.g. "netscape"
686
687 if ( (!(arc = DosQueryPathInfo(szFileMask,
688 FIL_STANDARD,
689 &fs3,
690 sizeof(fs3))))
691 // make sure it's not a directory
692 // and that it's not hidden
693 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
694 )
695 {
696 // copy
697 arc = CopyToBuffer(pszExecutable,
698 szFileMask,
699 cbExecutable);
700 // and stop
701 break;
702 }
703 else
704 arc = ERROR_FILE_NOT_FOUND;
705 // and search on
706
707 pszToken = strtok(NULL, ";");
708 };
709
710 free(pszPathCopy);
711 }
712 else
713 arc = ERROR_NOT_ENOUGH_MEMORY;
714
715 return arc;
716}
717
718/*
719 * FindFile:
720 * helper for doshFindExecutable.
721 *
722 *added V0.9.11 (2001-04-25) [umoeller]
723 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewrote second half for DosSearchPath replacement, which returns directories too
724 */
725
726STATIC APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
727 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
728 ULONG cbExecutable) // in: sizeof (*pszExecutable)
729{
730 APIRET arc = NO_ERROR;
731 FILESTATUS3 fs3;
732
733 if ( (strchr(pcszCommand, '\\'))
734 || (strchr(pcszCommand, ':'))
735 )
736 {
737 // looks like this is qualified:
738 arc = DosQueryPathInfo((PSZ)pcszCommand,
739 FIL_STANDARD,
740 &fs3,
741 sizeof(fs3));
742 if (!arc)
743 if (!(fs3.attrFile & FILE_DIRECTORY))
744 arc = CopyToBuffer(pszExecutable,
745 pcszCommand,
746 cbExecutable);
747 else
748 // directory:
749 arc = ERROR_INVALID_EXE_SIGNATURE;
750 }
751 else
752 {
753 // non-qualified:
754 /* arc = DosSearchPath(SEARCH_IGNORENETERRS
755 | SEARCH_ENVIRONMENT
756 | SEARCH_CUR_DIRECTORY,
757 "PATH",
758 (PSZ)pcszCommand,
759 pszExecutable,
760 cbExecutable); */
761 // The above is not useable. It returns directories
762 // on the path... for example, it returns \OS2\INSTALL\NETSCAPE
763 // if netscape is looked for. So we search manually... sigh.
764 // V0.9.16 (2001-10-08) [umoeller]
765 arc = doshSearchPath("PATH",
766 pcszCommand,
767 pszExecutable,
768 cbExecutable);
769 }
770
771 return arc;
772}
773
774/*
775 *@@ doshFindExecutable:
776 * this attempts to find an executable by doing the
777 * following:
778 *
779 * 1) If pcszCommand appears to be qualified (i.e. contains
780 * a backslash), this checks for whether the file exists.
781 * If it is a directory, ERROR_INVALID_EXE_SIGNATURE is
782 * returned.
783 *
784 * 2) If pcszCommand contains no backslash, this searches
785 * all directories on the PATH in order to find the full
786 * path of the executable. Starting with V0.9.16, we
787 * use doshSearchPath for that.
788 *
789 * papcszExtensions determines if additional searches are to be
790 * performed if the file doesn't exist (case 1) or doshSearchPath
791 * returned ERROR_FILE_NOT_FOUND (case 2).
792 * This must point to an array of strings specifying the extra
793 * extensions to search for.
794 *
795 * If both papcszExtensions and cExtensions are null, no
796 * extra searches are performed.
797 *
798 * Returns:
799 *
800 * -- NO_ERROR: pszExecutable has received the full path of
801 * the executable found by DosSearchPath.
802 *
803 * -- ERROR_FILE_NOT_FOUND
804 *
805 * -- ERROR_BUFFER_OVERFLOW: pcszCommand was found, but
806 * the pszExecutable buffer is too small to hold
807 * the full path.
808 *
809 * Example:
810 *
811 + const char *aExtensions[] = { "EXE",
812 + "COM",
813 + "CMD"
814 + };
815 + CHAR szExecutable[CCHMAXPATH];
816 + APIRET arc = doshFindExecutable("lvm",
817 + szExecutable,
818 + sizeof(szExecutable),
819 + aExtensions,
820 + 3);
821 *
822 *@@added V0.9.9 (2001-03-07) [umoeller]
823 *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
824 */
825
826APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
827 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
828 ULONG cbExecutable, // in: sizeof (*pszExecutable)
829 const char **papcszExtensions, // in: array of extensions (without dots)
830 ULONG cExtensions) // in: array item count
831{
832 APIRET arc = FindFile(pcszCommand,
833 pszExecutable,
834 cbExecutable);
835
836 if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
837 && (cExtensions) // any extra searches wanted?
838 )
839 {
840 // try additional things then
841 PSZ psz2 = (PSZ)malloc(strlen(pcszCommand) + 20);
842 if (psz2)
843 {
844 ULONG ul;
845 for (ul = 0;
846 ul < cExtensions;
847 ul++)
848 {
849 const char *pcszExtThis = papcszExtensions[ul];
850 sprintf(psz2,
851 "%s.%s",
852 pcszCommand,
853 pcszExtThis);
854 arc = FindFile(psz2,
855 pszExecutable,
856 cbExecutable);
857 if (arc != ERROR_FILE_NOT_FOUND)
858 break;
859 }
860
861 free(psz2);
862 }
863 else
864 arc = ERROR_NOT_ENOUGH_MEMORY;
865 }
866
867 return arc;
868}
869
870/*
871 *@@category: Helpers\Control program helpers\Partitions info
872 * functions for retrieving partition information directly
873 * from the partition tables on the disk. See doshGetPartitionsList.
874 */
875
876/********************************************************************
877 *
878 * Partition functions
879 *
880 ********************************************************************/
881
882/*
883 *@@ doshQueryDiskCount:
884 * returns the no. of physical disks installed
885 * on the system.
886 *
887 *@@added V0.9.0 [umoeller]
888 */
889
890UINT doshQueryDiskCount(VOID)
891{
892 USHORT usCount = 0;
893 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &usCount, 2, 0, 0);
894 return (usCount);
895}
896
897/*
898 *@@ doshType2FSName:
899 * this returns a static, zero-terminated string
900 * for the given FS type, or NULL if the type
901 * is unknown.
902 *
903 *@@added V0.9.0 [umoeller]
904 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewritten
905 */
906
907const char* doshType2FSName(unsigned char bFSType) // in: FS type
908{
909 switch (bFSType)
910 {
911 case 0x00: return "empty";
912 case 0x01: return "DOS 12-bit FAT < 10 Mb";
913 case 0x02: return "XENIX root file system";
914 case 0x03: return "XENIX /usr file system (obsolete)";
915 case 0x04: return "DOS 16-bit FAT < 32 Mb";
916 case 0x05: return "DOS 3.3+ extended partition";
917 case 0x06: return "DOS 3.31+ 16-bit FAT > 32 Mb";
918 case 0x07: return "HPFS/NTFS/QNX/Advanced Unix";
919 case 0x08: return "OS/2 1.0-1.3/AIX/Commodore/DELL";
920 case 0x09: return "AIX data/Coherent";
921 case 0x0A: return "OS/2 Boot Manager/OPUS/Coherent Swap";
922 case 0x0B: return "Windows95 with 32-bit FAT";
923 case 0x0C: return "Windows95 with 32-bit FAT (LBA)";
924 case 0x0E: return "Windows 95 VFAT (06h plus LBA)";
925 case 0x0F: return "Windows 95 VFAT (05h plus LBA)";
926 case 0x10: return "OPUS";
927 case 0x11: return "OS/2 Boot Manager hidden 12-bit FAT";
928 case 0x12: return "Compaq Diagnostics";
929 case 0x14: return "OS/2 Boot Manager hidden sub-32M 16-bit FAT";
930 case 0x16: return "OS/2 Boot Manager hidden over-32M 16-bit FAT";
931 case 0x17: return "OS/2 Boot Manager hidden HPFS";
932 case 0x18: return "AST special Windows swap file (\"Zero-Volt Suspend\")";
933 // case 0x21: reserved
934 // case 0x23: reserved
935 case 0x24: return "NEC MS-DOS 3.x";
936 // case 0x26: reserved
937 // case 0x31: reserved
938 // case 0x33: reserved
939 // case 0x34: reserved
940 // case 0x36: reserved
941 case 0x38: return "Theos";
942 case 0x3C: return "PowerQuest PartitionMagic recovery partition";
943 case 0x40: return "VENIX 80286";
944 case 0x41: return "Personal RISC Boot";
945 case 0x42: return "SFS (Secure File System) by Peter Gutmann";
946 case 0x50: return "OnTrack Disk Manager, read-only";
947 case 0x51: return "OnTrack Disk Manager, read/write";
948 case 0x52: return "CP/M or Microport System V/386";
949 case 0x53: return "OnTrack Disk Manager, write-only???";
950 case 0x54: return "OnTrack Disk Manager (DDO)";
951 case 0x56: return "GoldenBow VFeature";
952 case 0x61: return "SpeedStor";
953 case 0x63: return "Unix SysV/386, 386/ix or Mach, MtXinu BSD 4.3 on Mach or GNU HURD";
954 case 0x64: return "Novell NetWare 286";
955 case 0x65: return "Novell NetWare (3.11)";
956 case 0x67:
957 case 0x68:
958 case 0x69: return "Novell";
959 case 0x70: return "DiskSecure Multi-Boot";
960 // case 0x71: reserved
961 // case 0x73: reserved
962 // case 0x74: reserved
963 case 0x75: return "PC/IX";
964 // case 0x76: reserved
965 case 0x80: return "Minix v1.1 - 1.4a";
966 case 0x81: return "Minix v1.4b+ or Linux or Mitac Advanced Disk Manager";
967 case 0x82: return "Linux Swap or Prime";
968 case 0x83: return "Linux native file system (ext2fs/xiafs)";
969 case 0x84: return "OS/2-renumbered type 04h (hidden DOS C: drive)";
970 case 0x86: return "FAT16 volume/stripe set (Windows NT)";
971 case 0x87: return "HPFS Fault-Tolerant mirrored partition or NTFS volume/stripe set";
972 case 0x93: return "Amoeba file system";
973 case 0x94: return "Amoeba bad block table";
974 case 0xA0: return "Phoenix NoteBIOS Power Management \"Save-to-Disk\" partition";
975 // case 0xA1: reserved
976 // case 0xA3: reserved
977 // case 0xA4: reserved
978 case 0xA5: return "FreeBSD, BSD/386";
979 // case 0xA6: reserved
980 // case 0xB1: reserved
981 // case 0xB3: reserved
982 // case 0xB4: reserved
983 // case 0xB6: reserved
984 case 0xB7: return "BSDI file system (secondarily swap)";
985 case 0xB8: return "BSDI swap (secondarily file system)";
986 case 0xC1: return "DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT";
987 case 0xC4: return "DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT";
988 case 0xC6: return "DR DOS 6.0 LOGIN.EXE-secured Huge partition or NT corrupted FAT16 volume/stripe set";
989 case 0xC7: return "Syrinx Boot or corrupted NTFS volume/stripe set";
990 case 0xD8: return "CP/M-86";
991 case 0xDB: return "CP/M, Concurrent CP/M, Concurrent DOS, Convergent Technologies OS";
992 case 0xE1: return "SpeedStor 12-bit FAT extended partition";
993 case 0xE3: return "DOS read-only or Storage Dimensions";
994 case 0xE4: return "SpeedStor 16-bit FAT extended partition";
995 // case 0xE5: reserved
996 // case 0xE6: reserved
997 case 0xF1: return "Storage Dimensions";
998 case 0xF2: return "DOS 3.3+ secondary partition";
999 // case 0xF3: reserved
1000 case 0xF4: return "SpeedStor or Storage Dimensions";
1001 // case 0xF6: reserved
1002 case 0xFE: return "LANstep or IBM PS/2 IML";
1003 case 0xFF: return "Xenix bad block table";
1004 }
1005
1006 return NULL;
1007}
1008
1009/*
1010 * AppendPartition:
1011 * this appends the given partition information to
1012 * the given partition list. To do this, a new
1013 * PARTITIONINFO structure is created and appended
1014 * in a list (managed thru the PARTITIONINFO.pNext
1015 * items).
1016 *
1017 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
1018 * With each call of this function, this pointer is advanced
1019 * to point to the newly created PARTITIONINFO, so before
1020 * calling this function for the first time,
1021 *
1022 *@@added V0.9.0 [umoeller]
1023 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed truncated LVM names
1024 */
1025
1026STATIC APIRET AppendPartition(PARTITIONINFO **pppiFirst,
1027 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
1028 PUSHORT posCount, // in/out: partition count
1029 BYTE bDisk, // in: disk of partition
1030 const char *pszBootName, // in: boot partition name
1031 CHAR cLetter, // in/out: drive letter
1032 BYTE bFsType, // in: file system type
1033 BOOL fPrimary, // in: primary?
1034 BOOL fBootable,
1035 ULONG ulSectors) // in: no. of sectors
1036{
1037 APIRET arc = NO_ERROR;
1038 PPARTITIONINFO ppiNew;
1039 if (ppiNew = NEW(PARTITIONINFO))
1040 {
1041 ZERO(ppiNew);
1042
1043 // store data
1044 ppiNew->bDisk = bDisk;
1045 if ( (fBootable)
1046 && (pszBootName)
1047 )
1048 {
1049 // fixed truncated LVM names V0.9.20 (2002-08-10) [umoeller]
1050 strhncpy0(ppiNew->szBootName,
1051 pszBootName,
1052 sizeof(ppiNew->szBootName));
1053 }
1054 else
1055 ppiNew->szBootName[0] = 0;
1056 ppiNew->cLetter = cLetter;
1057 ppiNew->bFSType = bFsType;
1058 ppiNew->pcszFSType = doshType2FSName(bFsType);
1059 ppiNew->fPrimary = fPrimary;
1060 ppiNew->fBootable = fBootable;
1061 ppiNew->ulSize = ulSectors / 2048;
1062
1063 ppiNew->pNext = NULL;
1064
1065 (*posCount)++;
1066
1067 if (*pppiFirst == (PPARTITIONINFO)NULL)
1068 {
1069 // first call:
1070 *pppiFirst = ppiNew;
1071 *pppiThis = ppiNew;
1072 }
1073 else
1074 {
1075 // append to list
1076 (**pppiThis).pNext = ppiNew;
1077 *pppiThis = ppiNew;
1078 }
1079 }
1080 else
1081 arc = ERROR_NOT_ENOUGH_MEMORY;
1082
1083 return arc;
1084}
1085
1086#ifndef __XWPLITE__
1087
1088/*
1089 *@@ doshReadSector:
1090 * reads a physical disk sector.
1091 *
1092 * If NO_ERROR is returned, the sector contents
1093 * have been stored in *buff.
1094 *
1095 * Originally contributed by Dmitry A. Steklenev.
1096 *
1097 *@@added V0.9.0 [umoeller]
1098 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
1099 */
1100
1101APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
1102 void *buff,
1103 USHORT head,
1104 USHORT cylinder,
1105 USHORT sector)
1106{
1107 APIRET arc;
1108 HFILE dh = 0;
1109 char dn[256];
1110
1111 sprintf(dn, "%u:", disk);
1112 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
1113 {
1114 TRACKLAYOUT DiskIOParm;
1115 ULONG IOCtlDataLength = sizeof(DiskIOParm);
1116 ULONG IOCtlParmLength = 512;
1117
1118 DiskIOParm.bCommand = 0;
1119 DiskIOParm.usHead = head;
1120 DiskIOParm.usCylinder = cylinder;
1121 DiskIOParm.usFirstSector = 0;
1122 DiskIOParm.cSectors = 1;
1123 DiskIOParm.TrackTable[0].usSectorNumber = sector;
1124 DiskIOParm.TrackTable[0].usSectorSize = 512;
1125
1126 arc = DosDevIOCtl(dh,
1127 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
1128 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
1129 buff , IOCtlDataLength, &IOCtlDataLength);
1130
1131 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
1132 }
1133
1134 return arc;
1135}
1136
1137// Sector and Cylinder values are actually 6 bits and 10 bits:
1138//
1139// 1 1 1 1 1 1
1140// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
1141// ³c c c c c c c c C c S s s s s s³
1142// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
1143//
1144// The high two bits of the second byte are used as the high bits
1145// of a 10-bit value. This allows for as many as 1024 cylinders
1146// and 64 sectors per cylinder.
1147
1148/*
1149 * GetCyl:
1150 * get cylinder number.
1151 *
1152 * Originally contributed by Dmitry A. Steklenev.
1153 *
1154 *@@added V0.9.0 [umoeller]
1155 */
1156
1157STATIC USHORT GetCyl(USHORT rBeginSecCyl)
1158{
1159 return ( (rBeginSecCyl & 0x00C0) << 2)
1160 + ((rBeginSecCyl & 0xFF00) >> 8);
1161}
1162
1163/*
1164 * GetSec:
1165 * get sector number.
1166 *
1167 * Originally contributed by Dmitry A. Steklenev.
1168 *
1169 *@@added V0.9.0 [umoeller]
1170 */
1171
1172STATIC USHORT GetSec(USHORT rBeginSecCyl)
1173{
1174 return rBeginSecCyl & 0x003F;
1175}
1176
1177/*
1178 *@@ doshGetBootManager:
1179 * this goes thru the master boot records on all
1180 * disks to find the boot manager partition.
1181 *
1182 * Returns:
1183 *
1184 * -- NO_ERROR: boot manager found; in that case,
1185 * information about the boot manager
1186 * is written into *pusDisk, *pusPart,
1187 * *BmInfo. Any of these pointers can
1188 * be NULL if you're not interested.
1189 *
1190 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
1191 *
1192 * Originally contributed by Dmitry A. Steklenev.
1193 *
1194 *@@added V0.9.0 [umoeller]
1195 */
1196
1197APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
1198 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
1199 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
1200{
1201 APIRET arc = NO_ERROR;
1202 USHORT count = doshQueryDiskCount(); // Physical disk number
1203 MBR_INFO MBoot; // Master Boot
1204 USHORT usDisk;
1205
1206 if (count > 8) // Not above 8 disks
1207 count = 8;
1208
1209 for (usDisk = 1; usDisk <= count; usDisk++)
1210 {
1211 USHORT usPrim = 0;
1212
1213 // for each disk, read the MBR, which has the
1214 // primary partitions
1215 if ((arc = doshReadSector(usDisk,
1216 &MBoot,
1217 0, // head
1218 0, // cylinder
1219 1))) // sector
1220 return arc;
1221
1222 // scan primary partitions for whether
1223 // BootManager partition exists
1224 for (usPrim = 0; usPrim < 4; usPrim++)
1225 {
1226 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
1227 {
1228 // this is boot manager:
1229 if (pBmInfo)
1230 *pBmInfo = MBoot.sPrtnInfo[usPrim];
1231 if (pusPart)
1232 *pusPart = usPrim;
1233 if (pusDisk)
1234 *pusDisk = usDisk;
1235 // stop scanning
1236 return NO_ERROR;
1237 }
1238 }
1239 }
1240
1241 return (ERROR_NOT_SUPPORTED);
1242}
1243
1244/*
1245 * GetPrimaryPartitions:
1246 * this returns the primary partitions.
1247 *
1248 * This gets called from doshGetPartitionsList.
1249 *
1250 * Returns:
1251 *
1252 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
1253 *
1254 * Originally contributed by Dmitry A. Steklenev.
1255 *
1256 *@@added V0.9.0 [umoeller]
1257 */
1258
1259STATIC APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
1260 PARTITIONINFO **pppiThis,
1261 PUSHORT posCount, // in/out: partition count
1262 PCHAR pcLetter, // in/out: drive letter counter
1263 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
1264 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
1265 UINT iDisk) // in: system's physical disk count
1266{
1267 APIRET arc = NO_ERROR;
1268
1269 if (!pBmInfo)
1270 arc = ERROR_INVALID_PARAMETER;
1271 else
1272 {
1273 SYS_INFO MName[32]; // Name Space from Boot Manager
1274 memset(&MName, 0, sizeof(MName));
1275
1276 // read boot manager name table;
1277 // this is in the boot manager primary partition
1278 // at sector offset 3 (?!?)
1279 if (!(arc = doshReadSector(BmDisk,
1280 &MName,
1281 // head, cylinder, sector of bmgr primary partition:
1282 pBmInfo->bBeginHead,
1283 GetCyl(pBmInfo->rBeginSecCyl),
1284 GetSec(pBmInfo->rBeginSecCyl) + 3)))
1285 {
1286 // got bmgr name table:
1287 MBR_INFO MBoot; // Master Boot
1288 USHORT i;
1289
1290 // read master boot record of this disk
1291 if (!(arc = doshReadSector(iDisk,
1292 &MBoot,
1293 0, // head
1294 0, // cylinder
1295 1))) // sector
1296 {
1297 for (i = 0;
1298 i < 4; // there can be only four primary partitions
1299 i++)
1300 {
1301 // skip unused partition, BootManager or Extended partition
1302 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
1303 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A) // skip boot manager
1304 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x05) // skip extended partition
1305 )
1306 {
1307 BOOL fBootable = ( (pBmInfo)
1308 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
1309 );
1310 // store this partition
1311 if ((arc = AppendPartition(pppiFirst,
1312 pppiThis,
1313 posCount,
1314 iDisk,
1315 (fBootable)
1316 ? (char*)&MName[(iDisk - 1) * 4 + i].name
1317 : "",
1318 *pcLetter,
1319 MBoot.sPrtnInfo[i].bFileSysCode,
1320 TRUE, // primary
1321 fBootable,
1322 MBoot.sPrtnInfo[i].lTotalSects)))
1323 return arc;
1324 }
1325 }
1326 }
1327 }
1328 }
1329
1330 return arc;
1331}
1332
1333/*
1334 * GetLogicalDrives:
1335 * this returns info for the logical drives
1336 * in the extended partition. This gets called
1337 * from GetExtendedPartition.
1338 *
1339 * This gets called from GetExtendedPartition.
1340 *
1341 * Originally contributed by Dmitry A. Steklenev.
1342 *
1343 *@@added V0.9.0 [umoeller]
1344 */
1345
1346STATIC APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
1347 PARTITIONINFO **pppiThis,
1348 PUSHORT posCount,
1349 PCHAR pcLetter,
1350 PAR_INFO* PrInfo, // in: MBR entry of extended partition
1351 UINT PrDisk,
1352 PAR_INFO* BmInfo)
1353{
1354 APIRET arc = NO_ERROR;
1355 EXT_INFO MBoot; // Master Boot
1356 USHORT i;
1357
1358 if ((arc = doshReadSector(PrDisk,
1359 &MBoot,
1360 PrInfo->bBeginHead,
1361 GetCyl(PrInfo->rBeginSecCyl),
1362 GetSec(PrInfo->rBeginSecCyl))))
1363 return arc;
1364
1365 for (i = 0; i < 4; i++)
1366 {
1367 // skip unused partition or BootManager partition
1368 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
1369 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A)
1370 )
1371 {
1372 BOOL fBootable = FALSE;
1373 BOOL fAssignLetter = FALSE;
1374
1375 // special work around extended partition
1376 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1377 {
1378 if ((arc = GetLogicalDrives(pppiFirst,
1379 pppiThis,
1380 posCount,
1381 pcLetter,
1382 &MBoot.sPrtnInfo[i],
1383 PrDisk,
1384 BmInfo)))
1385 return arc;
1386
1387 continue;
1388 }
1389
1390 // raise driver letter if OS/2 would recognize this drive
1391 if ( (MBoot.sPrtnInfo[i].bFileSysCode < 0x75)
1392 )
1393 fAssignLetter = TRUE;
1394
1395 if (fAssignLetter)
1396 (*pcLetter)++;
1397
1398 fBootable = ( (BmInfo)
1399 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
1400 );
1401
1402 if ((arc = AppendPartition(pppiFirst,
1403 pppiThis,
1404 posCount,
1405 PrDisk,
1406 (fBootable)
1407 ? (char*)&MBoot.sBmNames[i].name
1408 : "",
1409 (fAssignLetter)
1410 ? *pcLetter
1411 : ' ',
1412 MBoot.sPrtnInfo[i].bFileSysCode,
1413 FALSE, // primary
1414 fBootable, // bootable
1415 MBoot.sPrtnInfo[i].lTotalSects)))
1416 return arc;
1417 }
1418 }
1419
1420 return NO_ERROR;
1421}
1422
1423/*
1424 * GetExtendedPartition:
1425 * this finds the extended partition on the given
1426 * drive and calls GetLogicalDrives in turn.
1427 *
1428 * This gets called from doshGetPartitionsList.
1429 *
1430 * Originally contributed by Dmitry A. Steklenev.
1431 *
1432 *@@added V0.9.0 [umoeller]
1433 */
1434
1435STATIC APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
1436 PARTITIONINFO **pppiThis,
1437 PUSHORT posCount,
1438 PCHAR pcLetter,
1439 PAR_INFO* BmInfo,
1440 UINT iDisk) // in: disk to query
1441{
1442 APIRET arc = NO_ERROR;
1443 MBR_INFO MBoot; // Master Boot
1444 USHORT i;
1445
1446 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
1447 return arc;
1448
1449 // go thru MBR entries to find extended partition
1450 for (i = 0;
1451 i < 4;
1452 i++)
1453 {
1454 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1455 {
1456 if ((arc = GetLogicalDrives(pppiFirst,
1457 pppiThis,
1458 posCount,
1459 pcLetter,
1460 &MBoot.sPrtnInfo[i],
1461 iDisk,
1462 BmInfo)))
1463 return arc;
1464 }
1465 }
1466
1467 return NO_ERROR;
1468}
1469
1470/*
1471 *@@ ReadFDiskPartitions:
1472 * helper for doshGetPartitionsList for non-LVM
1473 * systems.
1474 *
1475 * Originally contributed by Dmitry A. Steklenev.
1476 *
1477 *@@added V0.9.16 (2001-10-08) [umoeller]
1478 */
1479
1480STATIC APIRET ReadFDiskPartitions(PARTITIONINFO **ppPartitionInfos,
1481 USHORT *pcPartitions,
1482 PUSHORT pusContext) // out: error context
1483{
1484 APIRET arc = NO_ERROR;
1485
1486 PAR_INFO BmInfo; // BootManager partition
1487 USHORT usBmDisk; // BootManager disk
1488 USHORT cDisks = doshQueryDiskCount(); // physical disks count
1489 USHORT i;
1490
1491 CHAR cLetter = 'C'; // first drive letter
1492
1493 PARTITIONINFO *ppiTemp = NULL;
1494
1495 if (cDisks > 8) // Not above 8 disks
1496 cDisks = 8;
1497
1498 // get boot manager disk and info
1499 if ((arc = doshGetBootManager(&usBmDisk,
1500 NULL,
1501 &BmInfo)) != NO_ERROR)
1502 {
1503 *pusContext = 1;
1504 }
1505 else
1506 {
1507 // on each disk, read primary partitions
1508 for (i = 1; i <= cDisks; i++)
1509 {
1510 if ((arc = GetPrimaryPartitions(ppPartitionInfos,
1511 &ppiTemp,
1512 pcPartitions,
1513 &cLetter,
1514 usBmDisk,
1515 usBmDisk ? &BmInfo : 0,
1516 i)))
1517 {
1518 *pusContext = 2;
1519 }
1520 }
1521
1522 if (!arc && usBmDisk)
1523 {
1524 // boot manager found:
1525 // on each disk, read extended partition
1526 // with logical drives
1527 for (i = 1; i <= cDisks; i++)
1528 {
1529 if ((arc = GetExtendedPartition(ppPartitionInfos,
1530 &ppiTemp,
1531 pcPartitions,
1532 &cLetter,
1533 &BmInfo,
1534 i)))
1535 {
1536 *pusContext = 3;
1537 }
1538 }
1539 }
1540 } // end else if ((arc = doshGetBootManager(&usBmDisk,
1541
1542 return arc;
1543}
1544
1545#endif
1546
1547/*
1548 *@@ CleanPartitionInfos:
1549 *
1550 *@@added V0.9.9 (2001-04-07) [umoeller]
1551 */
1552
1553STATIC VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
1554{
1555 while (ppiThis)
1556 {
1557 PPARTITIONINFO ppiNext = ppiThis->pNext;
1558 free(ppiThis);
1559 ppiThis = ppiNext;
1560 }
1561}
1562
1563/*
1564 *@@ doshGetPartitionsList:
1565 * this returns lots of information about the
1566 * partitions on all physical disks, which is
1567 * read directly from the MBRs and partition
1568 * tables.
1569 *
1570 * If NO_ERROR is returned by this function,
1571 * *ppPartitionInfo points to a linked list of
1572 * PARTITIONINFO structures, which has
1573 * *pusPartitionCount items.
1574 *
1575 * In that case, use doshFreePartitionsList to
1576 * free the resources allocated by this function.
1577 *
1578 * What this function returns depends on whether
1579 * LVM is installed.
1580 *
1581 * -- If LVM.DLL is found on the LIBPATH, this opens
1582 * the LVM engine and returns the info from the
1583 * LVM engine in the PARTITIONINFO structures.
1584 * The partitions are then sorted by disk in
1585 * ascending order.
1586 *
1587 * -- Otherwise, we parse the partition tables
1588 * manually. The linked list then starts out with
1589 * all the primary partitions, followed by the
1590 * logical drives in the extended partitions.
1591 * This function attempts to guess the correct drive
1592 * letters and stores these with the PARTITIONINFO
1593 * items, but there's no guarantee that this is
1594 * correct. We correctly ignore Linux partitions here
1595 * and give all primary partitions the C: letter, but
1596 * I have no idea what happens with NTFS partitions,
1597 * since I have none.
1598 *
1599 * If an error != NO_ERROR is returned, *pusContext
1600 * will be set to one of the following:
1601 *
1602 * -- 1: boot manager not found
1603 *
1604 * -- 2: primary partitions error
1605 *
1606 * -- 3: secondary partitions error
1607 *
1608 * -- 0: something else.
1609 *
1610 * Originally contributed by Dmitry A. Steklenev.
1611 *
1612 *@@added V0.9.0 [umoeller]
1613 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
1614 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
1615 */
1616
1617APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
1618 PUSHORT pusContext) // out: error context
1619{
1620 APIRET arc = NO_ERROR;
1621
1622 PLVMINFO pLVMInfo = NULL;
1623
1624 PARTITIONINFO *pPartitionInfos = NULL; // linked list of all partitions
1625 USHORT cPartitions = 0; // bootable partition count
1626
1627 if (!ppList)
1628 return (ERROR_INVALID_PARAMETER);
1629
1630 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
1631 {
1632 // LVM installed:
1633 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
1634 &pPartitionInfos, // out: partitions array
1635 &cPartitions); // out: partitions count
1636 // copied to output below
1637
1638 if (arc)
1639 {
1640 // error: start over
1641 doshFreeLVMInfo(pLVMInfo);
1642 CleanPartitionInfos(pPartitionInfos);
1643 pPartitionInfos = NULL;
1644 cPartitions = 0;
1645 }
1646 }
1647
1648#ifndef __XWPLITE__
1649 if (arc)
1650 // LVM not installed, or failed:
1651 // parse partitions manually
1652 arc = ReadFDiskPartitions(&pPartitionInfos,
1653 &cPartitions,
1654 pusContext);
1655#endif
1656
1657 if (!arc)
1658 {
1659 // no error so far:
1660 *pusContext = 0;
1661
1662 *ppList = NEW(PARTITIONSLIST);
1663 if (!(*ppList))
1664 arc = ERROR_NOT_ENOUGH_MEMORY;
1665 else
1666 {
1667 ZERO(*ppList);
1668
1669 (*ppList)->pPartitionInfo = pPartitionInfos;
1670 (*ppList)->cPartitions = cPartitions;
1671
1672 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
1673 }
1674 }
1675
1676 if (arc)
1677 CleanPartitionInfos(pPartitionInfos);
1678
1679 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
1680
1681 return arc;
1682}
1683
1684/*
1685 *@@ doshFreePartitionsList:
1686 * this frees the resources allocated by
1687 * doshGetPartitionsList.
1688 *
1689 *@@added V0.9.0 [umoeller]
1690 */
1691
1692APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
1693{
1694 if (!ppList)
1695 return (ERROR_INVALID_PARAMETER);
1696 else
1697 {
1698 CleanPartitionInfos(ppList->pPartitionInfo);
1699 doshFreeLVMInfo(ppList->pLVMInfo);
1700 free(ppList);
1701 }
1702
1703 return NO_ERROR;
1704}
1705
1706/********************************************************************
1707 *
1708 * LVM declarations
1709 *
1710 ********************************************************************/
1711
1712/*
1713 *@@category: Helpers\Control program helpers\Partitions info\Quick LVM Interface
1714 * functions for transparently interfacing LVM.DLL.
1715 */
1716
1717typedef unsigned char BOOLEAN;
1718typedef unsigned short int CARDINAL16;
1719typedef unsigned long CARDINAL32;
1720typedef unsigned int CARDINAL;
1721typedef unsigned long DoubleWord;
1722
1723#ifdef ADDRESS
1724#undef ADDRESS
1725#endif
1726
1727typedef void* ADDRESS;
1728
1729#pragma pack(1)
1730
1731#define DISK_NAME_SIZE 20
1732#define FILESYSTEM_NAME_SIZE 20
1733#define PARTITION_NAME_SIZE 20
1734#define VOLUME_NAME_SIZE 20
1735
1736/*
1737 *@@ Drive_Control_Record:
1738 * invariant for a disk drive.
1739 *
1740 *@@added V0.9.9 (2001-04-07) [umoeller]
1741 */
1742
1743typedef struct _Drive_Control_Record
1744{
1745 CARDINAL32 Drive_Number; // OS/2 Drive Number for this drive.
1746 CARDINAL32 Drive_Size; // The total number of sectors on the drive.
1747 DoubleWord Drive_Serial_Number; // The serial number assigned to this drive. For info. purposes only.
1748 ADDRESS Drive_Handle; // Handle used for operations on the disk that this record corresponds to.
1749 CARDINAL32 Cylinder_Count; // The number of cylinders on the drive.
1750 CARDINAL32 Heads_Per_Cylinder; // The number of heads per cylinder for this drive.
1751 CARDINAL32 Sectors_Per_Track; // The number of sectors per track for this drive.
1752 BOOLEAN Drive_Is_PRM; // Set to TRUE if this drive is a PRM.
1753 BYTE Reserved[3]; // Alignment.
1754} Drive_Control_Record;
1755
1756/*
1757 *@@ Drive_Control_Array:
1758 * returned by the Get_Drive_Control_Data function
1759 *
1760 *@@added V0.9.9 (2001-04-07) [umoeller]
1761 */
1762
1763typedef struct _Drive_Control_Array
1764{
1765 Drive_Control_Record * Drive_Control_Data; // An array of drive control records.
1766 CARDINAL32 Count; // The number of entries in the array of drive control records.
1767} Drive_Control_Array;
1768
1769/*
1770 *@@ Drive_Information_Record:
1771 * defines the information that can be changed for a specific disk drive.
1772 *
1773 *@@added V0.9.9 (2001-04-07) [umoeller]
1774 */
1775
1776typedef struct _Drive_Information_Record
1777{
1778 CARDINAL32 Total_Available_Sectors; // The number of sectors on the disk which are not currently assigned to a partition.
1779 CARDINAL32 Largest_Free_Block_Of_Sectors; // The number of sectors in the largest contiguous block of available sectors.
1780 BOOLEAN Corrupt_Partition_Table; // If TRUE, then the partitioning information found on the drive is incorrect!
1781 BOOLEAN Unusable; // If TRUE, the drive's MBR is not accessible and the drive can not be partitioned.
1782 BOOLEAN IO_Error; // If TRUE, then the last I/O operation on this drive failed!
1783 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).
1784 char Drive_Name[DISK_NAME_SIZE]; // User assigned name for this disk drive.
1785} Drive_Information_Record;
1786
1787/*
1788 *@@ Partition_Information_Record:
1789 *
1790 *@@added V0.9.9 (2001-04-07) [umoeller]
1791 */
1792
1793typedef struct _Partition_Information_Record
1794{
1795 ADDRESS Partition_Handle;
1796 // The handle used to perform operations on this partition.
1797 ADDRESS Volume_Handle;
1798 // If this partition is part of a volume, this will be the handle of
1799 // the volume. If this partition is NOT part of a volume, then this
1800 // handle will be 0.
1801 ADDRESS Drive_Handle;
1802 // The handle for the drive this partition resides on.
1803 DoubleWord Partition_Serial_Number;
1804 // The serial number assigned to this partition.
1805 CARDINAL32 Partition_Start;
1806 // The LBA of the first sector of the partition.
1807 CARDINAL32 True_Partition_Size;
1808 // The total number of sectors comprising the partition.
1809 CARDINAL32 Usable_Partition_Size;
1810 // The size of the partition as reported to the IFSM. This is the
1811 // size of the partition less any LVM overhead.
1812 CARDINAL32 Boot_Limit;
1813 // The maximum number of sectors from this block of free space that
1814 // can be used to create a bootable partition if you allocate from the
1815 // beginning of the block of free space.
1816 BOOLEAN Spanned_Volume;
1817 // TRUE if this partition is part of a multi-partition volume.
1818 BOOLEAN Primary_Partition;
1819 // True or False. Any non-zero value here indicates that this partition
1820 // is a primary partition. Zero here indicates that this partition is
1821 // a "logical drive" - i.e. it resides inside of an extended partition.
1822 BYTE Active_Flag;
1823 // 80 = Partition is marked as being active.
1824 // 0 = Partition is not active.
1825 BYTE OS_Flag;
1826 // This field is from the partition table. It is known as the OS flag,
1827 // the Partition Type Field, Filesystem Type, and various other names.
1828 // Values of interest
1829 // If this field is: (values are in hex)
1830 // 07 = The partition is a compatibility partition formatted for use
1831 // with an installable filesystem, such as HPFS or JFS.
1832 // 00 = Unformatted partition
1833 // 01 = FAT12 filesystem is in use on this partition.
1834 // 04 = FAT16 filesystem is in use on this partition.
1835 // 0A = OS/2 Boot Manager Partition
1836 // 35 = LVM partition
1837 // 84 = OS/2 FAT16 partition which has been relabeled by Boot Manager to "Hide" it.
1838 BYTE Partition_Type;
1839 // 0 = Free Space
1840 // 1 = LVM Partition (Part of an LVM Volume.)
1841 // 2 = Compatibility Partition
1842 // All other values are reserved for future use.
1843 BYTE Partition_Status;
1844 // 0 = Free Space
1845 // 1 = In Use - i.e. already assigned to a volume.
1846 // 2 = Available - i.e. not currently assigned to a volume.
1847 BOOLEAN On_Boot_Manager_Menu;
1848 // Set to TRUE if this partition is not part of a Volume yet is on the
1849 // Boot Manager Menu.
1850 BYTE Reserved;
1851 // Alignment.
1852 char Volume_Drive_Letter;
1853 // The drive letter assigned to the volume that this partition is a part of.
1854 char Drive_Name[DISK_NAME_SIZE];
1855 // User assigned name for this disk drive.
1856 char File_System_Name[FILESYSTEM_NAME_SIZE];
1857 // The name of the filesystem in use on this partition, if it is known.
1858 char Partition_Name[PARTITION_NAME_SIZE];
1859 // The user assigned name for this partition.
1860 char Volume_Name[VOLUME_NAME_SIZE];
1861 // If this partition is part of a volume, then this will be the
1862 // name of the volume that this partition is a part of. If this
1863 // record represents free space, then the Volume_Name will be
1864 // "FREE SPACE xx", where xx is a unique numeric ID generated by
1865 // LVM.DLL. Otherwise it will be an empty string.
1866} Partition_Information_Record;
1867
1868// The following defines are for use with the Partition_Type field in the
1869// Partition_Information_Record.
1870#define FREE_SPACE_PARTITION 0
1871#define LVM_PARTITION 1
1872#define COMPATIBILITY_PARTITION 2
1873
1874// The following defines are for use with the Partition_Status field in the
1875// Partition_Information_Record.
1876#define PARTITION_IS_IN_USE 1
1877#define PARTITION_IS_AVAILABLE 2
1878#define PARTITION_IS_FREE_SPACE 0
1879
1880/*
1881 *@@ Partition_Information_Array:
1882 * returned by various functions in the LVM Engine.
1883 *
1884 *@@added V0.9.9 (2001-04-07) [umoeller]
1885 */
1886
1887typedef struct _Partition_Information_Array
1888{
1889 Partition_Information_Record * Partition_Array; // An array of Partition_Information_Records.
1890 CARDINAL32 Count; // The number of entries in the Partition_Array.
1891} Partition_Information_Array;
1892
1893/*
1894 *@@ Volume_Information_Record:
1895 * variable information for a volume.
1896 *
1897 *@@added V0.9.9 (2001-04-07) [umoeller]
1898 */
1899
1900typedef struct _Volume_Information_Record
1901{
1902 CARDINAL32 Volume_Size;
1903 // The number of sectors comprising the volume.
1904 CARDINAL32 Partition_Count;
1905 // The number of partitions which comprise this volume.
1906 CARDINAL32 Drive_Letter_Conflict;
1907 // 0 indicates that the drive letter preference for this volume is unique.
1908 // 1 indicates that the drive letter preference for this volume
1909 // is not unique, but this volume got its preferred drive letter anyway.
1910 // 2 indicates that the drive letter preference for this volume
1911 // is not unique, and this volume did NOT get its preferred drive letter.
1912 // 4 indicates that this volume is currently "hidden" - i.e. it has
1913 // no drive letter preference at the current time.
1914 BOOLEAN Compatibility_Volume;
1915 // TRUE if this is for a compatibility volume, FALSE otherwise.
1916 BOOLEAN Bootable;
1917 // Set to TRUE if this volume appears on the Boot Manager menu, or if it is
1918 // a compatibility volume and its corresponding partition is the first active
1919 // primary partition on the first drive.
1920 char Drive_Letter_Preference;
1921 // The drive letter that this volume desires to be.
1922 char Current_Drive_Letter;
1923 // The drive letter currently used to access this volume.
1924 // May be different than Drive_Letter_Preference if there was a conflict ( i.e. Drive_Letter_Preference
1925 // is already in use by another volume ).
1926 char Initial_Drive_Letter;
1927 // The drive letter assigned to this volume by the operating system
1928 // when LVM was started. This may be different from the
1929 // Drive_Letter_Preference if there were conflicts, and
1930 // may be different from the Current_Drive_Letter. This
1931 // will be 0x0 if the Volume did not exist when the LVM Engine
1932 // was opened (i.e. it was created during this LVM session).
1933 BOOLEAN New_Volume;
1934 // Set to FALSE if this volume existed before the LVM Engine was
1935 // opened. Set to TRUE if this volume was created after the LVM
1936 // Engine was opened.
1937 BYTE Status;
1938 // 0 = None.
1939 // 1 = Bootable
1940 // 2 = Startable
1941 // 3 = Installable.
1942 BYTE Reserved_1;
1943 char Volume_Name[VOLUME_NAME_SIZE];
1944 // The user assigned name for this volume.
1945 char File_System_Name[FILESYSTEM_NAME_SIZE];
1946 // The name of the filesystem in use on this partition, if it
1947 // is known.
1948} Volume_Information_Record;
1949
1950#pragma pack()
1951
1952/********************************************************************
1953 *
1954 * Quick LVM Interface API
1955 *
1956 ********************************************************************/
1957
1958/*
1959 *@@ LVMINFOPRIVATE:
1960 * private structure used by doshQueryLVMInfo.
1961 * This is what the LVMINFO pointer really
1962 * points to.
1963 *
1964 *@@added V0.9.9 (2001-04-07) [umoeller]
1965 */
1966
1967typedef struct _LVMINFOPRIVATE
1968{
1969 LVMINFO LVMInfo; // public structure (dosh.h)
1970
1971 // function pointers resolved from LVM.DLL
1972
1973 void (* _System Open_LVM_Engine)(BOOLEAN Ignore_CHS,
1974 CARDINAL32 *Error_Code);
1975
1976 void (* _System Free_Engine_Memory)(ADDRESS Object);
1977
1978 void (* _System Close_LVM_Engine)(void);
1979
1980 Drive_Control_Array (* _System
1981 Get_Drive_Control_Data)(CARDINAL32 *Error_Code);
1982
1983 Drive_Information_Record (* _System
1984 Get_Drive_Status)(ADDRESS Drive_Handle,
1985 CARDINAL32 *Error_Code);
1986
1987 Partition_Information_Array (* _System
1988 Get_Partitions)(ADDRESS Handle,
1989 CARDINAL32 *Error_Code);
1990
1991 Volume_Information_Record (*_System
1992 Get_Volume_Information)(ADDRESS Volume_Handle,
1993 CARDINAL32 *Error_Code);
1994
1995} LVMINFOPRIVATE, *PLVMINFOPRIVATE;
1996
1997#define LVM_ERROR_FIRST 20000
1998
1999/*
2000 *@@ doshQueryLVMInfo:
2001 * creates an LVMINFO structure if LVM is installed.
2002 * Returns that structure (which the caller must free
2003 * using doshFreeLVMInfo) or NULL if LVM.DLL was not
2004 * found along the LIBPATH.
2005 *
2006 *@@added V0.9.9 (2001-04-07) [umoeller]
2007 */
2008
2009APIRET doshQueryLVMInfo(PLVMINFO *ppLVMInfo)
2010{
2011 APIRET arc = NO_ERROR;
2012 CHAR szError[100];
2013 PLVMINFOPRIVATE pLVMInfo = NULL;
2014 HMODULE hmodLVM = NULLHANDLE;
2015
2016 if (!(arc = DosLoadModule(szError,
2017 sizeof(szError),
2018 "LVM",
2019 &hmodLVM)))
2020 {
2021 // got LVM.DLL:
2022 pLVMInfo = NEW(LVMINFOPRIVATE);
2023 if (!pLVMInfo)
2024 arc = ERROR_NOT_ENOUGH_MEMORY;
2025 else
2026 {
2027 // array of function pointers to be resolved from LVM.DLL
2028 RESOLVEFUNCTION aFunctions[] =
2029 {
2030 "Open_LVM_Engine", (PFN*)&pLVMInfo->Open_LVM_Engine,
2031 "Free_Engine_Memory", (PFN*)&pLVMInfo->Free_Engine_Memory,
2032 "Close_LVM_Engine", (PFN*)&pLVMInfo->Close_LVM_Engine,
2033 "Get_Drive_Control_Data", (PFN*)&pLVMInfo->Get_Drive_Control_Data,
2034 "Get_Drive_Status", (PFN*)&pLVMInfo->Get_Drive_Status,
2035 "Get_Partitions", (PFN*)&pLVMInfo->Get_Partitions,
2036 "Get_Volume_Information", (PFN*)&pLVMInfo->Get_Volume_Information
2037 };
2038 ULONG ul;
2039
2040 ZERO(pLVMInfo);
2041
2042 pLVMInfo->LVMInfo.hmodLVM = hmodLVM;
2043
2044 // now resolve function pointers
2045 for (ul = 0;
2046 ul < ARRAYITEMCOUNT(aFunctions);
2047 ul++)
2048 {
2049 PRESOLVEFUNCTION pFuncThis = &aFunctions[ul];
2050 arc = DosQueryProcAddr(hmodLVM,
2051 0, // ordinal, ignored
2052 (PSZ)pFuncThis->pcszFunctionName,
2053 pFuncThis->ppFuncAddress);
2054 if (!pFuncThis->ppFuncAddress)
2055 arc = ERROR_INVALID_NAME;
2056
2057 if (arc)
2058 break;
2059 }
2060 }
2061 }
2062
2063 if (arc)
2064 doshFreeLVMInfo((PLVMINFO)pLVMInfo);
2065 else
2066 *ppLVMInfo = (PLVMINFO)pLVMInfo;
2067
2068 return arc;
2069}
2070
2071/*
2072 *@@ doshReadLVMPartitions:
2073 * using the LVMINFO parameter from doshQueryLVMInfo,
2074 * builds an array of PARTITIONINFO structures with
2075 * the data returned from LVM.DLL.
2076 *
2077 *@@added V0.9.9 (2001-04-07) [umoeller]
2078 */
2079
2080APIRET doshReadLVMPartitions(PLVMINFO pInfo, // in: LVM info
2081 PPARTITIONINFO *ppPartitionInfo, // out: partitions array
2082 PUSHORT pcPartitions) // out: partitions count
2083{
2084 APIRET arc = NO_ERROR;
2085 CARDINAL32 Error = 0;
2086
2087 PARTITIONINFO *pPartitionInfos = NULL, // linked list of all partitions
2088 *ppiTemp = NULL;
2089 USHORT cPartitions = 0; // bootable partition count
2090 PLVMINFOPRIVATE pLVMInfo = (PLVMINFOPRIVATE)pInfo;
2091
2092 _Pmpf((__FUNCTION__ ": entering"));
2093
2094 if (!pLVMInfo)
2095 return (ERROR_INVALID_PARAMETER);
2096
2097 // initialize LVM engine
2098 pLVMInfo->Open_LVM_Engine(TRUE,
2099 &Error);
2100
2101 _Pmpf((" Open_LVM_Engine Error: %d"));
2102
2103 if (!Error)
2104 {
2105 Drive_Control_Array DCA = pLVMInfo->Get_Drive_Control_Data(&Error);
2106 // member records to be freed
2107
2108 _Pmpf((" Get_Drive_Control_Data Error: %d, drive count: %d", Error, DCA.Count));
2109
2110 if ( (!Error)
2111 && (DCA.Count)
2112 )
2113 {
2114 // DCA.Drive_Control_Data now contains drive information records;
2115 // this must be freed
2116 ULONG ulDisk;
2117
2118 for (ulDisk = 0;
2119 ulDisk < DCA.Count;
2120 ulDisk++)
2121 {
2122 Drive_Control_Record *pDriveControlRecord
2123 = &DCA.Drive_Control_Data[ulDisk];
2124 ADDRESS hDrive = pDriveControlRecord->Drive_Handle;
2125
2126 /* Drive_Information_Record pDriveInfoRecord
2127 = pLVMInfo->Get_Drive_Status(hDrive,
2128 &Error);
2129
2130 _Pmpf((" drive %d Get_Drive_Status Error: %d", ulDisk, Error));
2131
2132 if (!Error) */
2133 {
2134 Partition_Information_Array PIA
2135 = pLVMInfo->Get_Partitions(hDrive,
2136 &Error);
2137
2138 _Pmpf((" Get_Partitions Error: %d", Error));
2139
2140 if (!Error)
2141 {
2142 // PIA.Partition_Array now contains
2143 // Partition_Information_Record; must be freed
2144
2145 // now go thru partitions of this drive
2146 ULONG ulPart;
2147 for (ulPart = 0;
2148 ulPart < PIA.Count;
2149 ulPart++)
2150 {
2151 Partition_Information_Record *pPartition
2152 = &PIA.Partition_Array[ulPart];
2153 Volume_Information_Record VolumeInfo;
2154
2155 const char *pcszBootName = NULL; // for now
2156 BOOL fBootable = FALSE;
2157
2158 if (pPartition->Volume_Handle)
2159 {
2160 // this partition is part of a volume:
2161 // only then can it be bootable...
2162 // get the volume info
2163 VolumeInfo
2164 = pLVMInfo->Get_Volume_Information(pPartition->Volume_Handle,
2165 &Error);
2166 pcszBootName = VolumeInfo.Volume_Name;
2167
2168 fBootable = (VolumeInfo.Status == 1);
2169 }
2170
2171
2172 if (arc = AppendPartition(&pPartitionInfos,
2173 &ppiTemp,
2174 &cPartitions,
2175 ulDisk + 1,
2176 pcszBootName,
2177 pPartition->Volume_Drive_Letter,
2178 pPartition->OS_Flag, // FS type
2179 pPartition->Primary_Partition,
2180 fBootable,
2181 pPartition->True_Partition_Size))
2182 break;
2183 }
2184
2185 // clean up partitions
2186 pLVMInfo->Free_Engine_Memory(PIA.Partition_Array);
2187 }
2188 }
2189 /* else
2190 // error:
2191 break; */
2192 }
2193
2194 // clean up drive data
2195 pLVMInfo->Free_Engine_Memory(DCA.Drive_Control_Data);
2196 }
2197 }
2198
2199 // close LVM
2200 pLVMInfo->Close_LVM_Engine();
2201
2202 if (Error)
2203 {
2204 // if we got an error, return it with the
2205 // LVM error offset
2206 arc = LVM_ERROR_FIRST + Error;
2207
2208 CleanPartitionInfos(pPartitionInfos);
2209 }
2210
2211 if (!arc)
2212 {
2213 *ppPartitionInfo = pPartitionInfos;
2214 *pcPartitions = cPartitions;
2215 }
2216
2217 _Pmpf((__FUNCTION__ ": exiting, arg = %d", arc));
2218
2219 return arc;
2220}
2221
2222/*
2223 *@@ doshFreeLVMInfo:
2224 *
2225 *@@added V0.9.9 (2001-04-07) [umoeller]
2226 */
2227
2228VOID doshFreeLVMInfo(PLVMINFO pInfo)
2229{
2230 if (pInfo)
2231 {
2232 if (pInfo->hmodLVM)
2233 DosFreeModule(pInfo->hmodLVM);
2234
2235 free(pInfo);
2236 }
2237}
2238
2239/*
2240 *@@category: Helpers\Control program helpers\Wildcard matching
2241 * See doshMatch.
2242 */
2243
2244/* ******************************************************************
2245 *
2246 * Wildcard matching
2247 *
2248 ********************************************************************/
2249
2250/*
2251 * PerformMatch:
2252 * compares a single path component. The input strings must
2253 * not have slashes or backslashes in them.
2254 *
2255 * fHasDot must be true if pName contains at least one dot.
2256 *
2257 * Note that this function is recursive.
2258 */
2259
2260STATIC BOOL PerformMatch(PCSZ pMask,
2261 PCSZ pName,
2262 int fHasDot)
2263{
2264 while (TRUE)
2265 {
2266 // go thru the pMask char by char
2267 switch (*pMask)
2268 {
2269 case 0:
2270 // if we've reached the end of the mask,
2271 // we better have the end of the name too
2272 if (*pName == 0)
2273 return TRUE;
2274 return FALSE;
2275
2276 case '?':
2277 // a question mark matches one single character;
2278 // it does _not_ match a dot;
2279 // at the end of the component, it also matches
2280 // no characters
2281 if ( (*pName != '.')
2282 && (*pName != 0)
2283 )
2284 ++pName;
2285 ++pMask;
2286 break;
2287
2288 case '*':
2289 // asterisk matches zero or more characters
2290
2291 // skip extra asterisks
2292 /*
2293 do
2294 {
2295 ++pMask;
2296 } while (*pMask == '*');
2297 */
2298
2299 while (*(++pMask) == '*') // V0.9.20 (2002-07-25) [umoeller]
2300 ;
2301
2302 // pMask points to after '*';
2303 // pName is unchanged... so for each pName
2304 // that follows, check if it matches
2305 while (TRUE)
2306 {
2307 if (PerformMatch(pMask, pName, fHasDot))
2308 // the remainder matched:
2309 // then everything matches
2310 return TRUE;
2311
2312 if (*pName == 0)
2313 return FALSE;
2314
2315 // didn't match: try next pName
2316 ++pName;
2317 }
2318
2319 case '.':
2320 // a dot matches a dot only, even if the name doesn't
2321 // have one at the end
2322 ++pMask;
2323 if (*pName == '.')
2324 ++pName;
2325 else if ( (fHasDot)
2326 || (*pName != 0)
2327 )
2328 return FALSE;
2329 break;
2330
2331 default:
2332 if (*pMask++ != *pName++)
2333 return FALSE;
2334 break;
2335 }
2336 }
2337}
2338
2339/*
2340 *@@ doshMatchCase:
2341 * this matches '*' and '?' wildcards, similar to what
2342 * DosEditName does. However, this does not require a
2343 * file to be present, but works on strings only.
2344 *
2345 * Returns TRUE if the given name matches the given mask.
2346 *
2347 * This accepts both short and fully qualified masks and
2348 * names, but the following rules apply:
2349 *
2350 * -- Either both the mask and the name must be fully
2351 * qualified, or both must not. Otherwise the match fails.
2352 *
2353 * -- If fully qualified, only the last component may contain
2354 * wildcards.
2355 *
2356 * -- This compares WITH respect to case always. Upper-case
2357 * both the mask and the name before calling this, or
2358 * use doshMatch instead.
2359 *
2360 * -- As opposed to the WPS, this handles multiple dots in
2361 * filenames correctly. For example, the WPS will not
2362 * match "*.ZIP" against "whatever-0.9.3.zip", but this
2363 * one will.
2364 *
2365 * This replaces strhMatchOS2 which has been removed with
2366 * V0.9.16 and is a lot faster than the old code, which has
2367 * been completely rewritten.
2368 *
2369 *@@added V0.9.16 (2002-01-01) [umoeller]
2370 */
2371
2372BOOL doshMatchCase(const char *pcszMask, // in: mask (e.g. "*.TXT")
2373 const char *pcszName) // in: string to check (e.g. "TEST.TXT")
2374{
2375 BOOL brc = FALSE;
2376
2377 PCSZ pLastMaskComponent,
2378 pLastNameComponent;
2379
2380 ULONG cbMaskPath = 0,
2381 cbNamePath = 0;
2382
2383 if (pLastMaskComponent = strrchr(pcszMask, '\\'))
2384 {
2385 // length of path component
2386 cbMaskPath = pLastMaskComponent - pcszMask;
2387 pLastMaskComponent++;
2388 }
2389 else
2390 pLastMaskComponent = pcszMask;
2391
2392 if (pLastNameComponent = strrchr(pcszName, '\\'))
2393 {
2394 // length of path component
2395 cbNamePath = pLastNameComponent - pcszName;
2396 pLastNameComponent++;
2397 }
2398 else
2399 pLastNameComponent = pcszName;
2400
2401 // compare paths; if the lengths are different
2402 // or memcmp fails, we can't match
2403 if ( (cbMaskPath == cbNamePath) // can both be null
2404 && ( (cbMaskPath == 0)
2405 || (!memcmp(pcszMask, pcszName, cbMaskPath))
2406 )
2407 )
2408 {
2409 // alright, paths match:
2410 brc = PerformMatch(pLastMaskComponent,
2411 pLastNameComponent,
2412 // has dot?
2413 (strchr(pLastNameComponent, '.') != NULL));
2414
2415 }
2416
2417 return brc;
2418}
2419
2420/*
2421 *@@ doshMatchCaseNoPath:
2422 * like doshMatchCase, but is faster if you are sure that
2423 * neither pcszMask nor pcszName contain path separators
2424 * ("\" characters). In other words, this is for short
2425 * filenames.
2426 *
2427 *@@added V0.9.20 (2002-07-25) [umoeller]
2428 */
2429
2430BOOL doshMatchCaseNoPath(const char *pcszMask, // in: mask (e.g. "*.TXT")
2431 const char *pcszName) // in: string to check (e.g. "TEST.TXT")
2432{
2433 return PerformMatch(pcszMask,
2434 pcszName,
2435 // has dot?
2436 (strchr(pcszName, '.') != NULL));
2437}
2438
2439/*
2440 *@@ doshMatch:
2441 * like doshMatchCase, but compares without respect
2442 * to case.
2443 *
2444 *@@added V0.9.16 (2002-01-26) [umoeller]
2445 */
2446
2447BOOL doshMatch(const char *pcszMask, // in: mask (e.g. "*.TXT")
2448 const char *pcszName) // in: string to check (e.g. "TEST.TXT")
2449{
2450 ULONG cbMask = strlen(pcszMask),
2451 cbName = strlen(pcszName);
2452 PSZ pszMask = (PSZ)_alloca(cbMask + 1),
2453 pszName = (PSZ)_alloca(cbName + 1);
2454
2455 memcpy(pszMask, pcszMask, cbMask + 1);
2456 nlsUpper(pszMask);
2457 memcpy(pszName, pcszName, cbName + 1);
2458 nlsUpper(pszName);
2459
2460 return doshMatchCase(pszMask,
2461 pszName);
2462}
Note: See TracBrowser for help on using the repository browser.