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

Last change on this file since 472 was 415, checked in by pr, 12 years ago

Add JFS partition type description.

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