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

Last change on this file since 389 was 389, checked in by pr, 15 years ago

Fix debug missing value

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 84.0 KB
Line 
1
2/*
3 *@@sourcefile dosh2.c:
4 * dosh.c contains more Control Program helper functions.
5 *
6 * This file is new with V0.9.4 (2000-07-26) [umoeller].
7 *
8 * As opposed to the functions in dosh.c, these require
9 * linking against other helpers. As a result, these have
10 * been separated from dosh.c to allow linking against
11 * dosh.obj only.
12 *
13 * Function prefixes:
14 * -- dosh* Dos (Control Program) helper functions
15 *
16 * This has the same header as dosh.c, dosh.h.
17 *
18 * The partition functions in this file are based on
19 * code which has kindly been provided by Dmitry A. Steklenev.
20 * See doshGetPartitionsList for how to use these.
21 *
22 * Note: Version numbering in this file relates to XWorkplace version
23 * numbering.
24 *
25 *@@header "helpers\dosh.h"
26 *@@added V0.9.4 (2000-07-27) [umoeller]
27 */
28
29/*
30 * This file Copyright (C) 1997-2010 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
489 if ( (!pcszDir)
490 || (!(*pcszDir))
491 )
492 return ERROR_INVALID_PARAMETER;
493
494 if (pcszDir[1] == ':')
495 {
496 // drive given:
497 CHAR cDrive = toupper(*(pcszDir));
498 // change drive
499 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
500 // 1 = A:, 2 = B:, ...
501 }
502
503 if (!arc)
504 arc = DosSetCurrentDir((PSZ)pcszDir);
505
506 return arc; // V0.9.9 (2001-04-04) [umoeller]
507}
508
509/*
510 *@@ CopyToBuffer:
511 * little helper for copying a string to
512 * a target buffer with length checking.
513 *
514 * Returns:
515 *
516 * -- NO_ERROR
517 *
518 * -- ERROR_BUFFER_OVERFLOW if pszTarget does
519 * not have enough room to hold pcszSource
520 * (including the null terminator).
521 *
522 *@@added V0.9.16 (2001-10-08) [umoeller]
523 */
524
525STATIC APIRET CopyToBuffer(PSZ pszTarget, // out: target buffer
526 PCSZ pcszSource, // in: source string
527 ULONG cbTarget) // in: size of target buffer
528{
529 ULONG ulLength = strlen(pcszSource);
530 if (ulLength < cbTarget)
531 {
532 memcpy(pszTarget,
533 pcszSource,
534 ulLength + 1);
535 return NO_ERROR;
536 }
537
538 return ERROR_BUFFER_OVERFLOW;
539}
540
541/*
542 *@@ doshSearchPath:
543 * replacement for DosSearchPath.
544 *
545 * This looks along all directories which are
546 * specified in the value of the given environment
547 * variable if pcszFile is found.
548 *
549 * As opposed to the stupid DosSearchPath, this
550 * ignores subdirectories in the path particles.
551 * For example, DosSearchPath would usually not
552 * find an INSTALL file because \OS2 contains
553 * an INSTALL directory, or NETSCAPE because
554 * \OS2\INSTALL contains a NETSCAPE directory.
555 *
556 * Returns:
557 *
558 * -- NO_ERROR: pszExecutable has received the
559 * full path of pcszFile.
560 *
561 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
562 * in the specified path (or is a directory).
563 *
564 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
565 * the pszExecutable buffer is too small to hold
566 * the full path.
567 *
568 *@@added V0.9.16 (2001-10-08) [umoeller]
569 *@@changed V1.0.0 (2002-11-23) [umoeller]: allowing NULL pcszPath to search for "PATH"
570 */
571
572APIRET doshSearchPath(const char *pcszPath, // in: path variable name; if NULL, we use "PATH"
573 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
574 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
575 ULONG cbExecutable) // in: sizeof (*pszExecutable)
576{
577 APIRET arc = NO_ERROR;
578
579 // get the PATH value
580 PCSZ pcszPathValue;
581
582 if (!pcszPath)
583 pcszPath = "PATH"; // V1.0.0 (2002-11-23) [umoeller]
584
585 if (!(arc = DosScanEnv((PSZ)pcszPath,
586#if __cplusplus
587 &pcszPathValue)))
588#else
589 (PSZ*)&pcszPathValue)))
590#endif
591 {
592 // run thru the path components
593 PSZ pszPathCopy;
594 if (pszPathCopy = strdup(pcszPathValue))
595 {
596 PSZ pszToken = strtok(pszPathCopy, ";");
597 while (pszToken)
598 {
599 CHAR szFileMask[2*CCHMAXPATH];
600 FILESTATUS3 fs3;
601
602 sprintf(szFileMask,
603 "%s\\%s",
604 pszToken, // path particle
605 pcszFile); // e.g. "netscape"
606
607 if ( (!(arc = DosQueryPathInfo(szFileMask,
608 FIL_STANDARD,
609 &fs3,
610 sizeof(fs3))))
611 // make sure it's not a directory
612 // and that it's not hidden
613 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
614 )
615 {
616 // copy
617 arc = CopyToBuffer(pszExecutable,
618 szFileMask,
619 cbExecutable);
620 // and stop
621 break;
622 }
623 else
624 arc = ERROR_FILE_NOT_FOUND;
625 // and search on
626
627 pszToken = strtok(NULL, ";");
628 };
629
630 free(pszPathCopy);
631 }
632 else
633 arc = ERROR_NOT_ENOUGH_MEMORY;
634 }
635
636 return arc;
637}
638
639/*
640 *@@ doshSearchDirs:
641 * This looks along all directories which are
642 * specified in the passed directory list
643 * if pcszFile is found.
644 *
645 * As opposed to the stupid DosSearchPath, this
646 * ignores subdirectories in the path particles.
647 * For example, DosSearchPath would usually not
648 * find an INSTALL file because \OS2 contains
649 * an INSTALL directory, or NETSCAPE because
650 * \OS2\INSTALL contains a NETSCAPE directory.
651 *
652 * Returns:
653 *
654 * -- NO_ERROR: pszExecutable has received the
655 * full path of pcszFile.
656 *
657 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
658 * in the specified path (or is a directory).
659 *
660 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
661 * the pszExecutable buffer is too small to hold
662 * the full path.
663 *
664 *@@added V1.0.4 (2005-06-16) [chennecke]: blatantly stolen from doshSearchPath
665 */
666
667APIRET doshSearchDirs(const char *pcszDirList, // in: list of directories in PATH style
668 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
669 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
670 ULONG cbExecutable) // in: sizeof (*pszExecutable)
671{
672 APIRET arc = NO_ERROR;
673
674 // run thru the path components
675 PSZ pszPathCopy;
676 if (pszPathCopy = strdup(pcszDirList))
677 {
678 PSZ pszToken = strtok(pszPathCopy, ";");
679 while (pszToken)
680 {
681 CHAR szFileMask[2*CCHMAXPATH];
682 FILESTATUS3 fs3;
683
684 sprintf(szFileMask,
685 "%s\\%s",
686 pszToken, // path particle
687 pcszFile); // e.g. "netscape"
688
689 if ( (!(arc = DosQueryPathInfo(szFileMask,
690 FIL_STANDARD,
691 &fs3,
692 sizeof(fs3))))
693 // make sure it's not a directory
694 // and that it's not hidden
695 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
696 )
697 {
698 // copy
699 arc = CopyToBuffer(pszExecutable,
700 szFileMask,
701 cbExecutable);
702 // and stop
703 break;
704 }
705 else
706 arc = ERROR_FILE_NOT_FOUND;
707 // and search on
708
709 pszToken = strtok(NULL, ";");
710 };
711
712 free(pszPathCopy);
713 }
714 else
715 arc = ERROR_NOT_ENOUGH_MEMORY;
716
717 return arc;
718}
719
720/*
721 * FindFile:
722 * helper for doshFindExecutable.
723 *
724 *added V0.9.11 (2001-04-25) [umoeller]
725 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewrote second half for DosSearchPath replacement, which returns directories too
726 */
727
728STATIC APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
729 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
730 ULONG cbExecutable) // in: sizeof (*pszExecutable)
731{
732 APIRET arc = NO_ERROR;
733 FILESTATUS3 fs3;
734
735 if ( (strchr(pcszCommand, '\\'))
736 || (strchr(pcszCommand, ':'))
737 )
738 {
739 // looks like this is qualified:
740 arc = DosQueryPathInfo((PSZ)pcszCommand,
741 FIL_STANDARD,
742 &fs3,
743 sizeof(fs3));
744 if (!arc)
745 if (!(fs3.attrFile & FILE_DIRECTORY))
746 arc = CopyToBuffer(pszExecutable,
747 pcszCommand,
748 cbExecutable);
749 else
750 // directory:
751 arc = ERROR_INVALID_EXE_SIGNATURE;
752 }
753 else
754 {
755 // non-qualified:
756 /* arc = DosSearchPath(SEARCH_IGNORENETERRS
757 | SEARCH_ENVIRONMENT
758 | SEARCH_CUR_DIRECTORY,
759 "PATH",
760 (PSZ)pcszCommand,
761 pszExecutable,
762 cbExecutable); */
763 // The above is not useable. It returns directories
764 // on the path... for example, it returns \OS2\INSTALL\NETSCAPE
765 // if netscape is looked for. So we search manually... sigh.
766 // V0.9.16 (2001-10-08) [umoeller]
767 arc = doshSearchPath("PATH",
768 pcszCommand,
769 pszExecutable,
770 cbExecutable);
771 }
772
773 return arc;
774}
775
776/*
777 *@@ doshFindExecutable:
778 * this attempts to find an executable by doing the
779 * following:
780 *
781 * 1) If pcszCommand appears to be qualified (i.e. contains
782 * a backslash), this checks for whether the file exists.
783 * If it is a directory, ERROR_INVALID_EXE_SIGNATURE is
784 * returned.
785 *
786 * 2) If pcszCommand contains no backslash, this searches
787 * all directories on the PATH in order to find the full
788 * path of the executable. Starting with V0.9.16, we
789 * use doshSearchPath for that.
790 *
791 * papcszExtensions determines if additional searches are to be
792 * performed if the file doesn't exist (case 1) or doshSearchPath
793 * returned ERROR_FILE_NOT_FOUND (case 2).
794 * This must point to an array of strings specifying the extra
795 * extensions to search for.
796 *
797 * If both papcszExtensions and cExtensions are null, no
798 * extra searches are performed.
799 *
800 * Returns:
801 *
802 * -- NO_ERROR: pszExecutable has received the full path of
803 * the executable found by DosSearchPath.
804 *
805 * -- ERROR_FILE_NOT_FOUND
806 *
807 * -- ERROR_BUFFER_OVERFLOW: pcszCommand was found, but
808 * the pszExecutable buffer is too small to hold
809 * the full path.
810 *
811 * Example:
812 *
813 + const char *aExtensions[] = { "EXE",
814 + "COM",
815 + "CMD"
816 + };
817 + CHAR szExecutable[CCHMAXPATH];
818 + APIRET arc = doshFindExecutable("lvm",
819 + szExecutable,
820 + sizeof(szExecutable),
821 + aExtensions,
822 + 3);
823 *
824 *@@added V0.9.9 (2001-03-07) [umoeller]
825 *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
826 */
827
828APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
829 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
830 ULONG cbExecutable, // in: sizeof (*pszExecutable)
831 const char **papcszExtensions, // in: array of extensions (without dots)
832 ULONG cExtensions) // in: array item count
833{
834 APIRET arc = FindFile(pcszCommand,
835 pszExecutable,
836 cbExecutable);
837
838 if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
839 && (cExtensions) // any extra searches wanted?
840 )
841 {
842 // try additional things then
843 PSZ psz2;
844 if (psz2 = (PSZ)malloc(strlen(pcszCommand) + 20))
845 {
846 ULONG ul;
847 for (ul = 0;
848 ul < cExtensions;
849 ul++)
850 {
851 const char *pcszExtThis = papcszExtensions[ul];
852 sprintf(psz2,
853 "%s.%s",
854 pcszCommand,
855 pcszExtThis);
856 arc = FindFile(psz2,
857 pszExecutable,
858 cbExecutable);
859 if (arc != ERROR_FILE_NOT_FOUND)
860 break;
861 }
862
863 free(psz2);
864 }
865 else
866 arc = ERROR_NOT_ENOUGH_MEMORY;
867 }
868
869 return arc;
870}
871
872/*
873 *@@category: Helpers\Control program helpers\Partitions info
874 * functions for retrieving partition information directly
875 * from the partition tables on the disk. See doshGetPartitionsList.
876 */
877
878/********************************************************************
879 *
880 * Partition functions
881 *
882 ********************************************************************/
883
884/*
885 *@@ doshQueryDiskCount:
886 * returns the no. of physical disks installed
887 * on the system.
888 *
889 *@@added V0.9.0 [umoeller]
890 */
891
892UINT doshQueryDiskCount(VOID)
893{
894 USHORT usCount = 0;
895 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &usCount, 2, 0, 0);
896 return usCount;
897}
898
899/*
900 *@@ doshType2FSName:
901 * this returns a static, zero-terminated string
902 * for the given FS type, or NULL if the type
903 * is unknown.
904 *
905 *@@added V0.9.0 [umoeller]
906 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewritten
907 */
908
909const char* doshType2FSName(unsigned char bFSType) // in: FS type
910{
911 switch (bFSType)
912 {
913 case 0x00: return "empty";
914 case 0x01: return "DOS 12-bit FAT < 10 Mb";
915 case 0x02: return "XENIX root file system";
916 case 0x03: return "XENIX /usr file system (obsolete)";
917 case 0x04: return "DOS 16-bit FAT < 32 Mb";
918 case 0x05: return "DOS 3.3+ extended partition";
919 case 0x06: return "DOS 3.31+ 16-bit FAT > 32 Mb";
920 case 0x07: return "HPFS/NTFS/QNX/Advanced Unix";
921 case 0x08: return "OS/2 1.0-1.3/AIX/Commodore/DELL";
922 case 0x09: return "AIX data/Coherent";
923 case 0x0A: return "OS/2 Boot Manager/OPUS/Coherent Swap";
924 case 0x0B: return "Windows95 with 32-bit FAT";
925 case 0x0C: return "Windows95 with 32-bit FAT (LBA)";
926 case 0x0E: return "Windows 95 VFAT (06h plus LBA)";
927 case 0x0F: return "Windows 95 VFAT (05h plus LBA)";
928 case 0x10: return "OPUS";
929 case 0x11: return "OS/2 Boot Manager hidden 12-bit FAT";
930 case 0x12: return "Compaq Diagnostics";
931 case 0x14: return "OS/2 Boot Manager hidden sub-32M 16-bit FAT";
932 case 0x16: return "OS/2 Boot Manager hidden over-32M 16-bit FAT";
933 case 0x17: return "OS/2 Boot Manager hidden HPFS";
934 case 0x18: return "AST special Windows swap file (\"Zero-Volt Suspend\")";
935 // case 0x21: reserved
936 // case 0x23: reserved
937 case 0x24: return "NEC MS-DOS 3.x";
938 // case 0x26: reserved
939 // case 0x31: reserved
940 // case 0x33: reserved
941 // case 0x34: reserved
942 // case 0x36: reserved
943 case 0x38: return "Theos";
944 case 0x3C: return "PowerQuest PartitionMagic recovery partition";
945 case 0x40: return "VENIX 80286";
946 case 0x41: return "Personal RISC Boot";
947 case 0x42: return "SFS (Secure File System) by Peter Gutmann";
948 case 0x50: return "OnTrack Disk Manager, read-only";
949 case 0x51: return "OnTrack Disk Manager, read/write";
950 case 0x52: return "CP/M or Microport System V/386";
951 case 0x53: return "OnTrack Disk Manager, write-only???";
952 case 0x54: return "OnTrack Disk Manager (DDO)";
953 case 0x56: return "GoldenBow VFeature";
954 case 0x61: return "SpeedStor";
955 case 0x63: return "Unix SysV/386, 386/ix or Mach, MtXinu BSD 4.3 on Mach or GNU HURD";
956 case 0x64: return "Novell NetWare 286";
957 case 0x65: return "Novell NetWare (3.11)";
958 case 0x67:
959 case 0x68:
960 case 0x69: return "Novell";
961 case 0x70: return "DiskSecure Multi-Boot";
962 // case 0x71: reserved
963 // case 0x73: reserved
964 // case 0x74: reserved
965 case 0x75: return "PC/IX";
966 // case 0x76: reserved
967 case 0x80: return "Minix v1.1 - 1.4a";
968 case 0x81: return "Minix v1.4b+ or Linux or Mitac Advanced Disk Manager";
969 case 0x82: return "Linux Swap or Prime";
970 case 0x83: return "Linux native file system (ext2fs/xiafs)";
971 case 0x84: return "OS/2-renumbered type 04h (hidden DOS C: drive)";
972 case 0x86: return "FAT16 volume/stripe set (Windows NT)";
973 case 0x87: return "HPFS Fault-Tolerant mirrored partition or NTFS volume/stripe set";
974 case 0x93: return "Amoeba file system";
975 case 0x94: return "Amoeba bad block table";
976 case 0xA0: return "Phoenix NoteBIOS Power Management \"Save-to-Disk\" partition";
977 // case 0xA1: reserved
978 // case 0xA3: reserved
979 // case 0xA4: reserved
980 case 0xA5: return "FreeBSD, BSD/386";
981 // case 0xA6: reserved
982 // case 0xB1: reserved
983 // case 0xB3: reserved
984 // case 0xB4: reserved
985 // case 0xB6: reserved
986 case 0xB7: return "BSDI file system (secondarily swap)";
987 case 0xB8: return "BSDI swap (secondarily file system)";
988 case 0xC1: return "DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT";
989 case 0xC4: return "DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT";
990 case 0xC6: return "DR DOS 6.0 LOGIN.EXE-secured Huge partition or NT corrupted FAT16 volume/stripe set";
991 case 0xC7: return "Syrinx Boot or corrupted NTFS volume/stripe set";
992 case 0xD8: return "CP/M-86";
993 case 0xDB: return "CP/M, Concurrent CP/M, Concurrent DOS, Convergent Technologies OS";
994 case 0xE1: return "SpeedStor 12-bit FAT extended partition";
995 case 0xE3: return "DOS read-only or Storage Dimensions";
996 case 0xE4: return "SpeedStor 16-bit FAT extended partition";
997 // case 0xE5: reserved
998 // case 0xE6: reserved
999 case 0xF1: return "Storage Dimensions";
1000 case 0xF2: return "DOS 3.3+ secondary partition";
1001 // case 0xF3: reserved
1002 case 0xF4: return "SpeedStor or Storage Dimensions";
1003 // case 0xF6: reserved
1004 case 0xFE: return "LANstep or IBM PS/2 IML";
1005 case 0xFF: return "Xenix bad block table";
1006 }
1007
1008 return NULL;
1009}
1010
1011/*
1012 * AppendPartition:
1013 * this appends the given partition information to
1014 * the given partition list. To do this, a new
1015 * PARTITIONINFO structure is created and appended
1016 * in a list (managed thru the PARTITIONINFO.pNext
1017 * items).
1018 *
1019 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
1020 * With each call of this function, this pointer is advanced
1021 * to point to the newly created PARTITIONINFO, so before
1022 * calling this function for the first time,
1023 *
1024 *@@added V0.9.0 [umoeller]
1025 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed truncated LVM names
1026 */
1027
1028STATIC APIRET AppendPartition(PARTITIONINFO **pppiFirst,
1029 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
1030 PUSHORT posCount, // in/out: partition count
1031 BYTE bDisk, // in: disk of partition
1032 const char *pszBootName, // in: boot partition name
1033 CHAR cLetter, // in/out: drive letter
1034 BYTE bFsType, // in: file system type
1035 BOOL fPrimary, // in: primary?
1036 BOOL fBootable,
1037 ULONG ulSectors) // in: no. of sectors
1038{
1039 APIRET arc = NO_ERROR;
1040 PPARTITIONINFO ppiNew;
1041 if (ppiNew = NEW(PARTITIONINFO))
1042 {
1043 ZERO(ppiNew);
1044
1045 // store data
1046 ppiNew->bDisk = bDisk;
1047 if ( (fBootable)
1048 && (pszBootName)
1049 )
1050 {
1051 // fixed truncated LVM names V0.9.20 (2002-08-10) [umoeller]
1052 strhncpy0(ppiNew->szBootName,
1053 pszBootName,
1054 sizeof(ppiNew->szBootName));
1055 }
1056 else
1057 ppiNew->szBootName[0] = 0;
1058 ppiNew->cLetter = cLetter;
1059 ppiNew->bFSType = bFsType;
1060 ppiNew->pcszFSType = doshType2FSName(bFsType);
1061 ppiNew->fPrimary = fPrimary;
1062 ppiNew->fBootable = fBootable;
1063 ppiNew->ulSize = ulSectors / 2048;
1064
1065 ppiNew->pNext = NULL;
1066
1067 (*posCount)++;
1068
1069 if (*pppiFirst == (PPARTITIONINFO)NULL)
1070 {
1071 // first call:
1072 *pppiFirst = ppiNew;
1073 *pppiThis = ppiNew;
1074 }
1075 else
1076 {
1077 // append to list
1078 (**pppiThis).pNext = ppiNew;
1079 *pppiThis = ppiNew;
1080 }
1081 }
1082 else
1083 arc = ERROR_NOT_ENOUGH_MEMORY;
1084
1085 return arc;
1086}
1087
1088#ifndef __XWPLITE__
1089
1090/*
1091 *@@ doshReadSector:
1092 * reads a physical disk sector.
1093 *
1094 * If NO_ERROR is returned, the sector contents
1095 * have been stored in *buff.
1096 *
1097 * Originally contributed by Dmitry A. Steklenev.
1098 *
1099 *@@added V0.9.0 [umoeller]
1100 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
1101 */
1102
1103APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
1104 void *buff,
1105 USHORT head,
1106 USHORT cylinder,
1107 USHORT sector)
1108{
1109 APIRET arc;
1110 HFILE dh = 0;
1111 char dn[256];
1112
1113 sprintf(dn, "%u:", disk);
1114 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
1115 {
1116 TRACKLAYOUT DiskIOParm;
1117 ULONG IOCtlDataLength = sizeof(DiskIOParm);
1118 ULONG IOCtlParmLength = 512;
1119
1120 DiskIOParm.bCommand = 0;
1121 DiskIOParm.usHead = head;
1122 DiskIOParm.usCylinder = cylinder;
1123 DiskIOParm.usFirstSector = 0;
1124 DiskIOParm.cSectors = 1;
1125 DiskIOParm.TrackTable[0].usSectorNumber = sector;
1126 DiskIOParm.TrackTable[0].usSectorSize = 512;
1127
1128 arc = DosDevIOCtl(dh,
1129 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
1130 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
1131 buff , IOCtlDataLength, &IOCtlDataLength);
1132
1133 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
1134 }
1135
1136 return arc;
1137}
1138
1139// Sector and Cylinder values are actually 6 bits and 10 bits:
1140//
1141// 1 1 1 1 1 1
1142// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
1143// ³c c c c c c c c C c S s s s s s³
1144// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
1145//
1146// The high two bits of the second byte are used as the high bits
1147// of a 10-bit value. This allows for as many as 1024 cylinders
1148// and 64 sectors per cylinder.
1149
1150/*
1151 * GetCyl:
1152 * get cylinder number.
1153 *
1154 * Originally contributed by Dmitry A. Steklenev.
1155 *
1156 *@@added V0.9.0 [umoeller]
1157 */
1158
1159STATIC USHORT GetCyl(USHORT rBeginSecCyl)
1160{
1161 return ((rBeginSecCyl & 0x00C0) << 2)
1162 + ((rBeginSecCyl & 0xFF00) >> 8);
1163}
1164
1165/*
1166 * GetSec:
1167 * get sector number.
1168 *
1169 * Originally contributed by Dmitry A. Steklenev.
1170 *
1171 *@@added V0.9.0 [umoeller]
1172 */
1173
1174STATIC USHORT GetSec(USHORT rBeginSecCyl)
1175{
1176 return rBeginSecCyl & 0x003F;
1177}
1178
1179/*
1180 *@@ doshGetBootManager:
1181 * this goes thru the master boot records on all
1182 * disks to find the boot manager partition.
1183 *
1184 * Returns:
1185 *
1186 * -- NO_ERROR: boot manager found; in that case,
1187 * information about the boot manager
1188 * is written into *pusDisk, *pusPart,
1189 * *BmInfo. Any of these pointers can
1190 * be NULL if you're not interested.
1191 *
1192 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
1193 *
1194 * Originally contributed by Dmitry A. Steklenev.
1195 *
1196 *@@added V0.9.0 [umoeller]
1197 */
1198
1199APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
1200 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
1201 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
1202{
1203 APIRET arc = NO_ERROR;
1204 USHORT count = doshQueryDiskCount(); // Physical disk number
1205 MBR_INFO MBoot; // Master Boot
1206 USHORT usDisk;
1207
1208 if (count > 8) // Not above 8 disks
1209 count = 8;
1210
1211 for (usDisk = 1; usDisk <= count; usDisk++)
1212 {
1213 USHORT usPrim = 0;
1214
1215 // for each disk, read the MBR, which has the
1216 // primary partitions
1217 if ((arc = doshReadSector(usDisk,
1218 &MBoot,
1219 0, // head
1220 0, // cylinder
1221 1))) // sector
1222 return arc;
1223
1224 // scan primary partitions for whether
1225 // BootManager partition exists
1226 for (usPrim = 0; usPrim < 4; usPrim++)
1227 {
1228 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
1229 {
1230 // this is boot manager:
1231 if (pBmInfo)
1232 *pBmInfo = MBoot.sPrtnInfo[usPrim];
1233 if (pusPart)
1234 *pusPart = usPrim;
1235 if (pusDisk)
1236 *pusDisk = usDisk;
1237 // stop scanning
1238 return NO_ERROR;
1239 }
1240 }
1241 }
1242
1243 return ERROR_NOT_SUPPORTED;
1244}
1245
1246/*
1247 * GetPrimaryPartitions:
1248 * this returns the primary partitions.
1249 *
1250 * This gets called from doshGetPartitionsList.
1251 *
1252 * Returns:
1253 *
1254 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
1255 *
1256 * Originally contributed by Dmitry A. Steklenev.
1257 *
1258 *@@added V0.9.0 [umoeller]
1259 */
1260
1261STATIC APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
1262 PARTITIONINFO **pppiThis,
1263 PUSHORT posCount, // in/out: partition count
1264 PCHAR pcLetter, // in/out: drive letter counter
1265 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
1266 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
1267 UINT iDisk) // in: system's physical disk count
1268{
1269 APIRET arc = NO_ERROR;
1270
1271 if (!pBmInfo)
1272 arc = ERROR_INVALID_PARAMETER;
1273 else
1274 {
1275 SYS_INFO MName[32]; // Name Space from Boot Manager
1276 memset(&MName, 0, sizeof(MName));
1277
1278 // read boot manager name table;
1279 // this is in the boot manager primary partition
1280 // at sector offset 3 (?!?)
1281 if (!(arc = doshReadSector(BmDisk,
1282 &MName,
1283 // head, cylinder, sector of bmgr primary partition:
1284 pBmInfo->bBeginHead,
1285 GetCyl(pBmInfo->rBeginSecCyl),
1286 GetSec(pBmInfo->rBeginSecCyl) + 3)))
1287 {
1288 // got bmgr name table:
1289 MBR_INFO MBoot; // Master Boot
1290 USHORT i;
1291
1292 // read master boot record of this disk
1293 if (!(arc = doshReadSector(iDisk,
1294 &MBoot,
1295 0, // head
1296 0, // cylinder
1297 1))) // sector
1298 {
1299 for (i = 0;
1300 i < 4; // there can be only four primary partitions
1301 i++)
1302 {
1303 // skip unused partition, BootManager or Extended partition
1304 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
1305 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A) // skip boot manager
1306 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x05) // skip extended partition
1307 )
1308 {
1309 BOOL fBootable = ( (pBmInfo)
1310 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
1311 );
1312 // store this partition
1313 if ((arc = AppendPartition(pppiFirst,
1314 pppiThis,
1315 posCount,
1316 iDisk,
1317 (fBootable)
1318 ? (char*)&MName[(iDisk - 1) * 4 + i].name
1319 : "",
1320 *pcLetter,
1321 MBoot.sPrtnInfo[i].bFileSysCode,
1322 TRUE, // primary
1323 fBootable,
1324 MBoot.sPrtnInfo[i].lTotalSects)))
1325 return arc;
1326 }
1327 }
1328 }
1329 }
1330 }
1331
1332 return arc;
1333}
1334
1335/*
1336 * GetLogicalDrives:
1337 * this returns info for the logical drives
1338 * in the extended partition. This gets called
1339 * from GetExtendedPartition.
1340 *
1341 * This gets called from GetExtendedPartition.
1342 *
1343 * Originally contributed by Dmitry A. Steklenev.
1344 *
1345 *@@added V0.9.0 [umoeller]
1346 */
1347
1348STATIC APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
1349 PARTITIONINFO **pppiThis,
1350 PUSHORT posCount,
1351 PCHAR pcLetter,
1352 PAR_INFO* PrInfo, // in: MBR entry of extended partition
1353 UINT PrDisk,
1354 PAR_INFO* BmInfo)
1355{
1356 APIRET arc = NO_ERROR;
1357 EXT_INFO MBoot; // Master Boot
1358 USHORT i;
1359
1360 if ((arc = doshReadSector(PrDisk,
1361 &MBoot,
1362 PrInfo->bBeginHead,
1363 GetCyl(PrInfo->rBeginSecCyl),
1364 GetSec(PrInfo->rBeginSecCyl))))
1365 return arc;
1366
1367 for (i = 0; i < 4; i++)
1368 {
1369 // skip unused partition or BootManager partition
1370 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
1371 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A)
1372 )
1373 {
1374 BOOL fBootable = FALSE;
1375 BOOL fAssignLetter = FALSE;
1376
1377 // special work around extended partition
1378 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1379 {
1380 if ((arc = GetLogicalDrives(pppiFirst,
1381 pppiThis,
1382 posCount,
1383 pcLetter,
1384 &MBoot.sPrtnInfo[i],
1385 PrDisk,
1386 BmInfo)))
1387 return arc;
1388
1389 continue;
1390 }
1391
1392 // raise driver letter if OS/2 would recognize this drive
1393 if ( (MBoot.sPrtnInfo[i].bFileSysCode < 0x75)
1394 )
1395 fAssignLetter = TRUE;
1396
1397 if (fAssignLetter)
1398 (*pcLetter)++;
1399
1400 fBootable = ( (BmInfo)
1401 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
1402 );
1403
1404 if ((arc = AppendPartition(pppiFirst,
1405 pppiThis,
1406 posCount,
1407 PrDisk,
1408 (fBootable)
1409 ? (char*)&MBoot.sBmNames[i].name
1410 : "",
1411 (fAssignLetter)
1412 ? *pcLetter
1413 : ' ',
1414 MBoot.sPrtnInfo[i].bFileSysCode,
1415 FALSE, // primary
1416 fBootable, // bootable
1417 MBoot.sPrtnInfo[i].lTotalSects)))
1418 return arc;
1419 }
1420 }
1421
1422 return NO_ERROR;
1423}
1424
1425/*
1426 * GetExtendedPartition:
1427 * this finds the extended partition on the given
1428 * drive and calls GetLogicalDrives in turn.
1429 *
1430 * This gets called from doshGetPartitionsList.
1431 *
1432 * Originally contributed by Dmitry A. Steklenev.
1433 *
1434 *@@added V0.9.0 [umoeller]
1435 */
1436
1437STATIC APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
1438 PARTITIONINFO **pppiThis,
1439 PUSHORT posCount,
1440 PCHAR pcLetter,
1441 PAR_INFO* BmInfo,
1442 UINT iDisk) // in: disk to query
1443{
1444 APIRET arc = NO_ERROR;
1445 MBR_INFO MBoot; // Master Boot
1446 USHORT i;
1447
1448 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
1449 return arc;
1450
1451 // go thru MBR entries to find extended partition
1452 for (i = 0;
1453 i < 4;
1454 i++)
1455 {
1456 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1457 {
1458 if ((arc = GetLogicalDrives(pppiFirst,
1459 pppiThis,
1460 posCount,
1461 pcLetter,
1462 &MBoot.sPrtnInfo[i],
1463 iDisk,
1464 BmInfo)))
1465 return arc;
1466 }
1467 }
1468
1469 return NO_ERROR;
1470}
1471
1472/*
1473 *@@ ReadFDiskPartitions:
1474 * helper for doshGetPartitionsList for non-LVM
1475 * systems.
1476 *
1477 * Originally contributed by Dmitry A. Steklenev.
1478 *
1479 *@@added V0.9.16 (2001-10-08) [umoeller]
1480 */
1481
1482STATIC APIRET ReadFDiskPartitions(PARTITIONINFO **ppPartitionInfos,
1483 USHORT *pcPartitions,
1484 PUSHORT pusContext) // out: error context
1485{
1486 APIRET arc = NO_ERROR;
1487
1488 PAR_INFO BmInfo; // BootManager partition
1489 USHORT usBmDisk; // BootManager disk
1490 USHORT cDisks = doshQueryDiskCount(); // physical disks count
1491 USHORT i;
1492
1493 CHAR cLetter = 'C'; // first drive letter
1494
1495 PARTITIONINFO *ppiTemp = NULL;
1496
1497 if (cDisks > 8) // Not above 8 disks
1498 cDisks = 8;
1499
1500 // get boot manager disk and info
1501 if ((arc = doshGetBootManager(&usBmDisk,
1502 NULL,
1503 &BmInfo)) != NO_ERROR)
1504 {
1505 *pusContext = 1;
1506 }
1507 else
1508 {
1509 // on each disk, read primary partitions
1510 for (i = 1; i <= cDisks; i++)
1511 {
1512 if ((arc = GetPrimaryPartitions(ppPartitionInfos,
1513 &ppiTemp,
1514 pcPartitions,
1515 &cLetter,
1516 usBmDisk,
1517 usBmDisk ? &BmInfo : 0,
1518 i)))
1519 {
1520 *pusContext = 2;
1521 }
1522 }
1523
1524 if (!arc && usBmDisk)
1525 {
1526 // boot manager found:
1527 // on each disk, read extended partition
1528 // with logical drives
1529 for (i = 1; i <= cDisks; i++)
1530 {
1531 if ((arc = GetExtendedPartition(ppPartitionInfos,
1532 &ppiTemp,
1533 pcPartitions,
1534 &cLetter,
1535 &BmInfo,
1536 i)))
1537 {
1538 *pusContext = 3;
1539 }
1540 }
1541 }
1542 } // end else if ((arc = doshGetBootManager(&usBmDisk,
1543
1544 return arc;
1545}
1546
1547#endif
1548
1549/*
1550 *@@ CleanPartitionInfos:
1551 *
1552 *@@added V0.9.9 (2001-04-07) [umoeller]
1553 */
1554
1555STATIC VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
1556{
1557 while (ppiThis)
1558 {
1559 PPARTITIONINFO ppiNext = ppiThis->pNext;
1560 free(ppiThis);
1561 ppiThis = ppiNext;
1562 }
1563}
1564
1565/*
1566 *@@ doshGetPartitionsList:
1567 * this returns lots of information about the
1568 * partitions on all physical disks, which is
1569 * read directly from the MBRs and partition
1570 * tables.
1571 *
1572 * If NO_ERROR is returned by this function,
1573 * *ppPartitionInfo points to a linked list of
1574 * PARTITIONINFO structures, which has
1575 * *pusPartitionCount items.
1576 *
1577 * In that case, use doshFreePartitionsList to
1578 * free the resources allocated by this function.
1579 *
1580 * What this function returns depends on whether
1581 * LVM is installed.
1582 *
1583 * -- If LVM.DLL is found on the LIBPATH, this opens
1584 * the LVM engine and returns the info from the
1585 * LVM engine in the PARTITIONINFO structures.
1586 * The partitions are then sorted by disk in
1587 * ascending order.
1588 *
1589 * -- Otherwise, we parse the partition tables
1590 * manually. The linked list then starts out with
1591 * all the primary partitions, followed by the
1592 * logical drives in the extended partitions.
1593 * This function attempts to guess the correct drive
1594 * letters and stores these with the PARTITIONINFO
1595 * items, but there's no guarantee that this is
1596 * correct. We correctly ignore Linux partitions here
1597 * and give all primary partitions the C: letter, but
1598 * I have no idea what happens with NTFS partitions,
1599 * since I have none.
1600 *
1601 * If an error != NO_ERROR is returned, *pusContext
1602 * will be set to one of the following:
1603 *
1604 * -- 1: boot manager not found
1605 *
1606 * -- 2: primary partitions error
1607 *
1608 * -- 3: secondary partitions error
1609 *
1610 * -- 0: something else.
1611 *
1612 * Originally contributed by Dmitry A. Steklenev.
1613 *
1614 *@@added V0.9.0 [umoeller]
1615 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
1616 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
1617 */
1618
1619APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
1620 PUSHORT pusContext) // out: error context
1621{
1622 APIRET arc = NO_ERROR;
1623
1624 PLVMINFO pLVMInfo = NULL;
1625
1626 PARTITIONINFO *pPartitionInfos = NULL; // linked list of all partitions
1627 USHORT cPartitions = 0; // bootable partition count
1628
1629 if (!ppList)
1630 return ERROR_INVALID_PARAMETER;
1631
1632 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
1633 {
1634 // LVM installed:
1635 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
1636 &pPartitionInfos, // out: partitions array
1637 &cPartitions); // out: partitions count
1638 // copied to output below
1639
1640 if (arc)
1641 {
1642 // error: start over
1643 doshFreeLVMInfo(pLVMInfo);
1644 CleanPartitionInfos(pPartitionInfos);
1645 pPartitionInfos = NULL;
1646 cPartitions = 0;
1647 }
1648 }
1649
1650#ifndef __XWPLITE__
1651 if (arc)
1652 // LVM not installed, or failed:
1653 // parse partitions manually
1654 arc = ReadFDiskPartitions(&pPartitionInfos,
1655 &cPartitions,
1656 pusContext);
1657#endif
1658
1659 if (!arc)
1660 {
1661 // no error so far:
1662 *pusContext = 0;
1663
1664 *ppList = NEW(PARTITIONSLIST);
1665 if (!(*ppList))
1666 arc = ERROR_NOT_ENOUGH_MEMORY;
1667 else
1668 {
1669 ZERO(*ppList);
1670
1671 (*ppList)->pPartitionInfo = pPartitionInfos;
1672 (*ppList)->cPartitions = cPartitions;
1673
1674 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
1675 }
1676 }
1677
1678 if (arc)
1679 CleanPartitionInfos(pPartitionInfos);
1680
1681 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
1682
1683 return arc;
1684}
1685
1686/*
1687 *@@ doshFreePartitionsList:
1688 * this frees the resources allocated by
1689 * doshGetPartitionsList.
1690 *
1691 *@@added V0.9.0 [umoeller]
1692 */
1693
1694APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
1695{
1696 if (!ppList)
1697 return ERROR_INVALID_PARAMETER;
1698
1699 CleanPartitionInfos(ppList->pPartitionInfo);
1700 doshFreeLVMInfo(ppList->pLVMInfo);
1701 free(ppList);
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", Error));
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.