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

Last change on this file since 322 was 317, checked in by pr, 19 years ago

Move doshIsWarp4 to dosh2 and query Syslevel to determine W4/MCP difference

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