source: trunk/src/helpers/dosh2.c

Last change on this file 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.1 KB
RevLine 
[8]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/*
[415]30 * This file Copyright (C) 1997-2013 Ulrich M”ller,
[8]31 * Dmitry A. Steklenev.
[14]32 * This file is part of the "XWorkplace helpers" source package.
33 * This is free software; you can redistribute it and/or modify
[8]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
[14]48#define INCL_DOSMODULEMGR
49#define INCL_DOSPROCESS
50#define INCL_DOSSESMGR
51#define INCL_DOSQUEUES
52#define INCL_DOSMISC
53#define INCL_DOSDEVICES
[8]54#define INCL_DOSDEVIOCTL
55#define INCL_DOSERRORS
56#include <os2.h>
[14]57
[8]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"
[56]66#include "helpers\ensure.h"
[318]67#include "helpers\level.h"
[127]68#include "helpers\nls.h"
[56]69#include "helpers\standards.h"
[8]70#include "helpers\stringh.h"
71
72#pragma hdrstop
73
74/*
75 *@@category: Helpers\Control program helpers\Miscellaneous
76 */
77
78/* ******************************************************************
[14]79 *
80 * Miscellaneous
81 *
[8]82 ********************************************************************/
83
84/*
[318]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/*
[364]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/*
[8]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{
[14]272 APIRET arc = NO_ERROR;
[8]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 )
[14]284 arc = ERROR_CURRENT_DIRECTORY;
[8]285 }
286
287 // check drive first
288 if (*(pcszFile + 1) == ':')
289 {
290 CHAR cDrive = toupper(*pcszFile);
[14]291 double d;
[8]292 // drive specified:
293 strcpy(szPath, pcszFile);
294 szPath[0] = toupper(*pcszFile);
[14]295 arc = doshQueryDiskFree(cDrive - 'A' + 1, &d);
[8]296 }
297 else
298 {
299 // no drive specified: take current
300 ULONG ulDriveNum = 0,
301 ulDriveMap = 0;
[14]302 arc = DosQueryCurrentDisk(&ulDriveNum, &ulDriveMap);
[8]303 szPath[0] = ((UCHAR)ulDriveNum) + 'A' - 1;
304 szPath[1] = ':';
305 strcpy(&szPath[2], pcszFile);
306 }
307
[14]308 if (arc == NO_ERROR)
309 {
310 fIsFAT = doshIsFileOnFAT(szPath);
[8]311
[14]312 pszInvalid = (fIsFAT)
313 ? "<>|+=:;,\"/[] " // invalid characters in FAT
314 : "<>|:\"/"; // invalid characters in IFS's
[8]315
[14]316 // now separate path components
317 p1 = &szPath[2]; // advance past ':'
[8]318
[14]319 do {
[8]320
[14]321 if (*p1 == '\\')
322 p1++;
[8]323
[14]324 p2 = strchr(p1, '\\');
325 if (p2 == NULL)
326 p2 = p1 + strlen(p1);
[8]327
[14]328 if (p1 != p2)
329 {
330 LONG lDotOfs = -1,
331 lAfterDot = -1;
332 ULONG cbFile,
333 ul;
334 PSZ pSource = szComponent;
[8]335
[14]336 strncpy(szComponent, p1, p2-p1);
337 szComponent[p2-p1] = 0;
338 cbFile = strlen(szComponent);
[8]339
[14]340 // now check each path component
341 for (ul = 0; ul < cbFile; ul++)
[8]342 {
[14]343 if (fIsFAT)
[8]344 {
[14]345 // on FAT: only 8 characters allowed before dot
346 if (*pSource == '.')
347 {
348 lDotOfs = ul;
349 lAfterDot = 0;
350 if (ul > 7)
[238]351 return ERROR_FILENAME_EXCED_RANGE;
[14]352 }
[8]353 }
[14]354 // and check for invalid characters
355 if (strchr(pszInvalid, *pSource) != NULL)
[238]356 return ERROR_INVALID_NAME;
[14]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)
[238]366 return ERROR_FILENAME_EXCED_RANGE;
[14]367 }
[8]368 }
369
[14]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
[8]373 if (fIsFAT)
[14]374 if (lDotOfs == -1) // dot not found:
375 if (cbFile > 8)
[238]376 return ERROR_FILENAME_EXCED_RANGE;
[8]377 }
378
[14]379 // go for next component
380 p1 = p2+1;
381 } while (*p2);
382 }
[8]383
[167]384 return arc;
[8]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.
[51]392 *
[8]393 * pszTarget must be at least the same size as pszSource.
394 * If (fIsFAT), the file name will be made FAT-compliant (8+3).
[51]395 *
[8]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,
[54]413 pTarget = pszTarget;
[8]414
[54]415 const char *pcszInvalid = (fIsFAT)
416 ? "*<>|+=:;,\"/\\[] " // invalid characters in FAT
417 : "*<>|:\"/\\"; // invalid characters in IFS's
418
[8]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
[54]436 if (strchr(pcszInvalid, *pSource) == NULL)
[8]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
[167]470 return brc;
[8]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.
[54]481 *
[265]482 *@@changed V0.9.9 (2001-04-04) [umoeller]: this returned an error even if none occurred, fixed
[8]483 */
484
485APIRET doshSetCurrentDir(const char *pcszDir)
486{
[54]487 APIRET arc = NO_ERROR;
[238]488
489 if ( (!pcszDir)
490 || (!(*pcszDir))
491 )
492 return ERROR_INVALID_PARAMETER;
493
494 if (pcszDir[1] == ':')
[8]495 {
[238]496 // drive given:
497 CHAR cDrive = toupper(*(pcszDir));
498 // change drive
499 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
500 // 1 = A:, 2 = B:, ...
501 }
[8]502
[238]503 if (!arc)
[54]504 arc = DosSetCurrentDir((PSZ)pcszDir);
505
[167]506 return arc; // V0.9.9 (2001-04-04) [umoeller]
[8]507}
508
509/*
[108]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
[222]525STATIC APIRET CopyToBuffer(PSZ pszTarget, // out: target buffer
[142]526 PCSZ pcszSource, // in: source string
527 ULONG cbTarget) // in: size of target buffer
[108]528{
529 ULONG ulLength = strlen(pcszSource);
530 if (ulLength < cbTarget)
531 {
532 memcpy(pszTarget,
533 pcszSource,
534 ulLength + 1);
[169]535 return NO_ERROR;
[108]536 }
537
[240]538 return ERROR_BUFFER_OVERFLOW;
[108]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]
[229]569 *@@changed V1.0.0 (2002-11-23) [umoeller]: allowing NULL pcszPath to search for "PATH"
[108]570 */
571
[229]572APIRET doshSearchPath(const char *pcszPath, // in: path variable name; if NULL, we use "PATH"
[108]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
[110]580 PCSZ pcszPathValue;
[229]581
582 if (!pcszPath)
583 pcszPath = "PATH"; // V1.0.0 (2002-11-23) [umoeller]
584
[108]585 if (!(arc = DosScanEnv((PSZ)pcszPath,
[110]586#if __cplusplus
587 &pcszPathValue)))
588#else
589 (PSZ*)&pcszPathValue)))
590#endif
[108]591 {
592 // run thru the path components
593 PSZ pszPathCopy;
[110]594 if (pszPathCopy = strdup(pcszPathValue))
[108]595 {
596 PSZ pszToken = strtok(pszPathCopy, ";");
[110]597 while (pszToken)
[108]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
[167]636 return arc;
[108]637}
638
639/*
[286]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/*
[64]721 * FindFile:
722 * helper for doshFindExecutable.
723 *
724 *added V0.9.11 (2001-04-25) [umoeller]
[108]725 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewrote second half for DosSearchPath replacement, which returns directories too
[64]726 */
727
[222]728STATIC APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
[142]729 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
730 ULONG cbExecutable) // in: sizeof (*pszExecutable)
[64]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))
[108]746 arc = CopyToBuffer(pszExecutable,
747 pcszCommand,
748 cbExecutable);
[64]749 else
750 // directory:
751 arc = ERROR_INVALID_EXE_SIGNATURE;
752 }
753 else
[108]754 {
[64]755 // non-qualified:
[108]756 /* arc = DosSearchPath(SEARCH_IGNORENETERRS
757 | SEARCH_ENVIRONMENT
758 | SEARCH_CUR_DIRECTORY,
[64]759 "PATH",
760 (PSZ)pcszCommand,
761 pszExecutable,
[108]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 }
[64]772
[167]773 return arc;
[64]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.
[108]783 * If it is a directory, ERROR_INVALID_EXE_SIGNATURE is
784 * returned.
[64]785 *
[108]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.
[64]790 *
791 * papcszExtensions determines if additional searches are to be
[108]792 * performed if the file doesn't exist (case 1) or doshSearchPath
[64]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 *
[108]800 * Returns:
[64]801 *
[108]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 *
[64]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
[240]843 PSZ psz2;
844 if (psz2 = (PSZ)malloc(strlen(pcszCommand) + 20))
[64]845 {
846 ULONG ul;
847 for (ul = 0;
848 ul < cExtensions;
849 ul++)
850 {
851 const char *pcszExtThis = papcszExtensions[ul];
[108]852 sprintf(psz2,
853 "%s.%s",
854 pcszCommand,
855 pcszExtThis);
[64]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
[167]869 return arc;
[64]870}
871
872/*
[8]873 *@@category: Helpers\Control program helpers\Partitions info
[21]874 * functions for retrieving partition information directly
875 * from the partition tables on the disk. See doshGetPartitionsList.
[8]876 */
877
878/********************************************************************
[14]879 *
880 * Partition functions
881 *
[8]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{
[108]894 USHORT usCount = 0;
895 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &usCount, 2, 0, 0);
[238]896 return usCount;
[8]897}
898
899/*
900 *@@ doshType2FSName:
901 * this returns a static, zero-terminated string
[108]902 * for the given FS type, or NULL if the type
903 * is unknown.
[8]904 *
905 *@@added V0.9.0 [umoeller]
[108]906 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewritten
[8]907 */
908
[54]909const char* doshType2FSName(unsigned char bFSType) // in: FS type
[8]910{
911 switch (bFSType)
912 {
[108]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
[415]942 case 0x35: return "JFS";
[108]943 // case 0x36: reserved
944 case 0x38: return "Theos";
945 case 0x3C: return "PowerQuest PartitionMagic recovery partition";
946 case 0x40: return "VENIX 80286";
947 case 0x41: return "Personal RISC Boot";
948 case 0x42: return "SFS (Secure File System) by Peter Gutmann";
949 case 0x50: return "OnTrack Disk Manager, read-only";
950 case 0x51: return "OnTrack Disk Manager, read/write";
951 case 0x52: return "CP/M or Microport System V/386";
952 case 0x53: return "OnTrack Disk Manager, write-only???";
953 case 0x54: return "OnTrack Disk Manager (DDO)";
954 case 0x56: return "GoldenBow VFeature";
955 case 0x61: return "SpeedStor";
956 case 0x63: return "Unix SysV/386, 386/ix or Mach, MtXinu BSD 4.3 on Mach or GNU HURD";
957 case 0x64: return "Novell NetWare 286";
958 case 0x65: return "Novell NetWare (3.11)";
959 case 0x67:
960 case 0x68:
961 case 0x69: return "Novell";
962 case 0x70: return "DiskSecure Multi-Boot";
963 // case 0x71: reserved
964 // case 0x73: reserved
965 // case 0x74: reserved
966 case 0x75: return "PC/IX";
967 // case 0x76: reserved
968 case 0x80: return "Minix v1.1 - 1.4a";
969 case 0x81: return "Minix v1.4b+ or Linux or Mitac Advanced Disk Manager";
970 case 0x82: return "Linux Swap or Prime";
971 case 0x83: return "Linux native file system (ext2fs/xiafs)";
972 case 0x84: return "OS/2-renumbered type 04h (hidden DOS C: drive)";
973 case 0x86: return "FAT16 volume/stripe set (Windows NT)";
974 case 0x87: return "HPFS Fault-Tolerant mirrored partition or NTFS volume/stripe set";
975 case 0x93: return "Amoeba file system";
976 case 0x94: return "Amoeba bad block table";
977 case 0xA0: return "Phoenix NoteBIOS Power Management \"Save-to-Disk\" partition";
978 // case 0xA1: reserved
979 // case 0xA3: reserved
980 // case 0xA4: reserved
981 case 0xA5: return "FreeBSD, BSD/386";
982 // case 0xA6: reserved
983 // case 0xB1: reserved
984 // case 0xB3: reserved
985 // case 0xB4: reserved
986 // case 0xB6: reserved
987 case 0xB7: return "BSDI file system (secondarily swap)";
988 case 0xB8: return "BSDI swap (secondarily file system)";
989 case 0xC1: return "DR DOS 6.0 LOGIN.EXE-secured 12-bit FAT";
990 case 0xC4: return "DR DOS 6.0 LOGIN.EXE-secured 16-bit FAT";
991 case 0xC6: return "DR DOS 6.0 LOGIN.EXE-secured Huge partition or NT corrupted FAT16 volume/stripe set";
992 case 0xC7: return "Syrinx Boot or corrupted NTFS volume/stripe set";
993 case 0xD8: return "CP/M-86";
994 case 0xDB: return "CP/M, Concurrent CP/M, Concurrent DOS, Convergent Technologies OS";
995 case 0xE1: return "SpeedStor 12-bit FAT extended partition";
996 case 0xE3: return "DOS read-only or Storage Dimensions";
997 case 0xE4: return "SpeedStor 16-bit FAT extended partition";
998 // case 0xE5: reserved
999 // case 0xE6: reserved
1000 case 0xF1: return "Storage Dimensions";
1001 case 0xF2: return "DOS 3.3+ secondary partition";
1002 // case 0xF3: reserved
1003 case 0xF4: return "SpeedStor or Storage Dimensions";
1004 // case 0xF6: reserved
1005 case 0xFE: return "LANstep or IBM PS/2 IML";
1006 case 0xFF: return "Xenix bad block table";
1007 }
[8]1008
[108]1009 return NULL;
[8]1010}
1011
1012/*
1013 * AppendPartition:
1014 * this appends the given partition information to
1015 * the given partition list. To do this, a new
1016 * PARTITIONINFO structure is created and appended
1017 * in a list (managed thru the PARTITIONINFO.pNext
1018 * items).
1019 *
1020 * pppiThis must be a pointer to a pointer to a PARTITIONINFO.
1021 * With each call of this function, this pointer is advanced
1022 * to point to the newly created PARTITIONINFO, so before
1023 * calling this function for the first time,
1024 *
1025 *@@added V0.9.0 [umoeller]
[200]1026 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed truncated LVM names
[8]1027 */
1028
[222]1029STATIC APIRET AppendPartition(PARTITIONINFO **pppiFirst,
[142]1030 PARTITIONINFO **pppiThis, // in/out: partition info; pointer will be advanced
1031 PUSHORT posCount, // in/out: partition count
1032 BYTE bDisk, // in: disk of partition
1033 const char *pszBootName, // in: boot partition name
1034 CHAR cLetter, // in/out: drive letter
1035 BYTE bFsType, // in: file system type
1036 BOOL fPrimary, // in: primary?
1037 BOOL fBootable,
1038 ULONG ulSectors) // in: no. of sectors
[8]1039{
1040 APIRET arc = NO_ERROR;
[200]1041 PPARTITIONINFO ppiNew;
1042 if (ppiNew = NEW(PARTITIONINFO))
[8]1043 {
[56]1044 ZERO(ppiNew);
1045
[8]1046 // store data
1047 ppiNew->bDisk = bDisk;
[200]1048 if ( (fBootable)
1049 && (pszBootName)
1050 )
[8]1051 {
[200]1052 // fixed truncated LVM names V0.9.20 (2002-08-10) [umoeller]
1053 strhncpy0(ppiNew->szBootName,
1054 pszBootName,
1055 sizeof(ppiNew->szBootName));
[8]1056 }
1057 else
1058 ppiNew->szBootName[0] = 0;
1059 ppiNew->cLetter = cLetter;
1060 ppiNew->bFSType = bFsType;
[108]1061 ppiNew->pcszFSType = doshType2FSName(bFsType);
[8]1062 ppiNew->fPrimary = fPrimary;
1063 ppiNew->fBootable = fBootable;
1064 ppiNew->ulSize = ulSectors / 2048;
1065
1066 ppiNew->pNext = NULL;
1067
1068 (*posCount)++;
1069
1070 if (*pppiFirst == (PPARTITIONINFO)NULL)
1071 {
1072 // first call:
1073 *pppiFirst = ppiNew;
1074 *pppiThis = ppiNew;
1075 }
1076 else
1077 {
1078 // append to list
1079 (**pppiThis).pNext = ppiNew;
1080 *pppiThis = ppiNew;
1081 }
1082 }
1083 else
1084 arc = ERROR_NOT_ENOUGH_MEMORY;
1085
[167]1086 return arc;
[8]1087}
1088
[108]1089#ifndef __XWPLITE__
1090
1091/*
1092 *@@ doshReadSector:
1093 * reads a physical disk sector.
1094 *
1095 * If NO_ERROR is returned, the sector contents
1096 * have been stored in *buff.
1097 *
1098 * Originally contributed by Dmitry A. Steklenev.
1099 *
1100 *@@added V0.9.0 [umoeller]
1101 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
1102 */
1103
1104APIRET doshReadSector(USHORT disk, // in: physical disk no. (1, 2, 3, ...)
1105 void *buff,
1106 USHORT head,
1107 USHORT cylinder,
1108 USHORT sector)
1109{
1110 APIRET arc;
1111 HFILE dh = 0;
1112 char dn[256];
1113
1114 sprintf(dn, "%u:", disk);
1115 if (!(arc = DosPhysicalDisk(INFO_GETIOCTLHANDLE, &dh, 2, dn, 3)))
1116 {
1117 TRACKLAYOUT DiskIOParm;
1118 ULONG IOCtlDataLength = sizeof(DiskIOParm);
1119 ULONG IOCtlParmLength = 512;
1120
1121 DiskIOParm.bCommand = 0;
1122 DiskIOParm.usHead = head;
1123 DiskIOParm.usCylinder = cylinder;
1124 DiskIOParm.usFirstSector = 0;
1125 DiskIOParm.cSectors = 1;
1126 DiskIOParm.TrackTable[0].usSectorNumber = sector;
1127 DiskIOParm.TrackTable[0].usSectorSize = 512;
1128
1129 arc = DosDevIOCtl(dh,
1130 IOCTL_PHYSICALDISK, PDSK_READPHYSTRACK,
1131 &DiskIOParm, IOCtlParmLength, &IOCtlParmLength,
1132 buff , IOCtlDataLength, &IOCtlDataLength);
1133
1134 DosPhysicalDisk(INFO_FREEIOCTLHANDLE, 0, 0, &dh, 2);
1135 }
1136
[167]1137 return arc;
[108]1138}
1139
[8]1140// Sector and Cylinder values are actually 6 bits and 10 bits:
1141//
1142// 1 1 1 1 1 1
1143// Ú5Â4Â3Â2Â1Â0Â9Â8Â7Â6Â5Â4Â3Â2Â1ÂÄ¿
1144// ³c c c c c c c c C c S s s s s s³
1145// ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
1146//
1147// The high two bits of the second byte are used as the high bits
1148// of a 10-bit value. This allows for as many as 1024 cylinders
1149// and 64 sectors per cylinder.
1150
1151/*
1152 * GetCyl:
1153 * get cylinder number.
1154 *
[108]1155 * Originally contributed by Dmitry A. Steklenev.
[8]1156 *
1157 *@@added V0.9.0 [umoeller]
1158 */
1159
[222]1160STATIC USHORT GetCyl(USHORT rBeginSecCyl)
[8]1161{
[238]1162 return ((rBeginSecCyl & 0x00C0) << 2)
1163 + ((rBeginSecCyl & 0xFF00) >> 8);
[8]1164}
1165
1166/*
1167 * GetSec:
1168 * get sector number.
1169 *
[108]1170 * Originally contributed by Dmitry A. Steklenev.
[8]1171 *
1172 *@@added V0.9.0 [umoeller]
1173 */
1174
[222]1175STATIC USHORT GetSec(USHORT rBeginSecCyl)
[8]1176{
1177 return rBeginSecCyl & 0x003F;
1178}
1179
1180/*
1181 *@@ doshGetBootManager:
1182 * this goes thru the master boot records on all
[56]1183 * disks to find the boot manager partition.
[8]1184 *
1185 * Returns:
[56]1186 *
[8]1187 * -- NO_ERROR: boot manager found; in that case,
1188 * information about the boot manager
1189 * is written into *pusDisk, *pusPart,
1190 * *BmInfo. Any of these pointers can
1191 * be NULL if you're not interested.
[56]1192 *
[8]1193 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
1194 *
[108]1195 * Originally contributed by Dmitry A. Steklenev.
[8]1196 *
1197 *@@added V0.9.0 [umoeller]
1198 */
1199
1200APIRET doshGetBootManager(USHORT *pusDisk, // out: if != NULL, boot manager disk (1, 2, ...)
1201 USHORT *pusPart, // out: if != NULL, index of bmgr primary partition (0-3)
[56]1202 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
[8]1203{
1204 APIRET arc = NO_ERROR;
1205 USHORT count = doshQueryDiskCount(); // Physical disk number
1206 MBR_INFO MBoot; // Master Boot
1207 USHORT usDisk;
1208
1209 if (count > 8) // Not above 8 disks
1210 count = 8;
1211
1212 for (usDisk = 1; usDisk <= count; usDisk++)
1213 {
1214 USHORT usPrim = 0;
1215
1216 // for each disk, read the MBR, which has the
1217 // primary partitions
[56]1218 if ((arc = doshReadSector(usDisk,
1219 &MBoot,
1220 0, // head
1221 0, // cylinder
1222 1))) // sector
[167]1223 return arc;
[8]1224
[56]1225 // scan primary partitions for whether
1226 // BootManager partition exists
[8]1227 for (usPrim = 0; usPrim < 4; usPrim++)
1228 {
1229 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
1230 {
[56]1231 // this is boot manager:
1232 if (pBmInfo)
1233 *pBmInfo = MBoot.sPrtnInfo[usPrim];
[8]1234 if (pusPart)
1235 *pusPart = usPrim;
1236 if (pusDisk)
1237 *pusDisk = usDisk;
[56]1238 // stop scanning
[169]1239 return NO_ERROR;
[8]1240 }
1241 }
1242 }
1243
[238]1244 return ERROR_NOT_SUPPORTED;
[8]1245}
1246
1247/*
1248 * GetPrimaryPartitions:
1249 * this returns the primary partitions.
1250 *
1251 * This gets called from doshGetPartitionsList.
1252 *
[56]1253 * Returns:
[8]1254 *
[56]1255 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
1256 *
[108]1257 * Originally contributed by Dmitry A. Steklenev.
[8]1258 *
1259 *@@added V0.9.0 [umoeller]
1260 */
1261
[222]1262STATIC APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
[142]1263 PARTITIONINFO **pppiThis,
1264 PUSHORT posCount, // in/out: partition count
1265 PCHAR pcLetter, // in/out: drive letter counter
1266 UINT BmDisk, // in: physical disk (1, 2, 3, ...) of boot manager or null
1267 PAR_INFO* pBmInfo, // in: info returned by doshGetBootManager or NULL
1268 UINT iDisk) // in: system's physical disk count
[8]1269{
[54]1270 APIRET arc = NO_ERROR;
[8]1271
[56]1272 if (!pBmInfo)
[54]1273 arc = ERROR_INVALID_PARAMETER;
1274 else
1275 {
1276 SYS_INFO MName[32]; // Name Space from Boot Manager
1277 memset(&MName, 0, sizeof(MName));
[8]1278
[56]1279 // read boot manager name table;
1280 // this is in the boot manager primary partition
1281 // at sector offset 3 (?!?)
[54]1282 if (!(arc = doshReadSector(BmDisk,
1283 &MName,
[56]1284 // head, cylinder, sector of bmgr primary partition:
1285 pBmInfo->bBeginHead,
1286 GetCyl(pBmInfo->rBeginSecCyl),
1287 GetSec(pBmInfo->rBeginSecCyl) + 3)))
[54]1288 {
[56]1289 // got bmgr name table:
[54]1290 MBR_INFO MBoot; // Master Boot
1291 USHORT i;
[8]1292
[54]1293 // read master boot record of this disk
[56]1294 if (!(arc = doshReadSector(iDisk,
1295 &MBoot,
1296 0, // head
1297 0, // cylinder
1298 1))) // sector
[54]1299 {
1300 for (i = 0;
1301 i < 4; // there can be only four primary partitions
1302 i++)
1303 {
1304 // skip unused partition, BootManager or Extended partition
1305 if ( (MBoot.sPrtnInfo[i].bFileSysCode) // skip unused
[108]1306 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A) // skip boot manager
1307 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x05) // skip extended partition
[54]1308 )
1309 {
[56]1310 BOOL fBootable = ( (pBmInfo)
1311 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
[54]1312 );
1313 // store this partition
1314 if ((arc = AppendPartition(pppiFirst,
1315 pppiThis,
1316 posCount,
1317 iDisk,
1318 (fBootable)
1319 ? (char*)&MName[(iDisk - 1) * 4 + i].name
1320 : "",
1321 *pcLetter,
1322 MBoot.sPrtnInfo[i].bFileSysCode,
1323 TRUE, // primary
1324 fBootable,
1325 MBoot.sPrtnInfo[i].lTotalSects)))
[167]1326 return arc;
[54]1327 }
1328 }
1329 }
[8]1330 }
1331 }
[54]1332
[167]1333 return arc;
[8]1334}
1335
1336/*
1337 * GetLogicalDrives:
1338 * this returns info for the logical drives
1339 * in the extended partition. This gets called
1340 * from GetExtendedPartition.
1341 *
1342 * This gets called from GetExtendedPartition.
1343 *
[108]1344 * Originally contributed by Dmitry A. Steklenev.
[8]1345 *
1346 *@@added V0.9.0 [umoeller]
1347 */
1348
[222]1349STATIC APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
[142]1350 PARTITIONINFO **pppiThis,
1351 PUSHORT posCount,
1352 PCHAR pcLetter,
1353 PAR_INFO* PrInfo, // in: MBR entry of extended partition
1354 UINT PrDisk,
1355 PAR_INFO* BmInfo)
[8]1356{
1357 APIRET arc = NO_ERROR;
1358 EXT_INFO MBoot; // Master Boot
1359 USHORT i;
1360
[54]1361 if ((arc = doshReadSector(PrDisk,
1362 &MBoot,
1363 PrInfo->bBeginHead,
[8]1364 GetCyl(PrInfo->rBeginSecCyl),
1365 GetSec(PrInfo->rBeginSecCyl))))
[167]1366 return arc;
[8]1367
1368 for (i = 0; i < 4; i++)
1369 {
1370 // skip unused partition or BootManager partition
1371 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
[108]1372 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A)
[8]1373 )
1374 {
1375 BOOL fBootable = FALSE;
1376 BOOL fAssignLetter = FALSE;
1377
1378 // special work around extended partition
[108]1379 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
[8]1380 {
1381 if ((arc = GetLogicalDrives(pppiFirst,
1382 pppiThis,
1383 posCount,
1384 pcLetter,
1385 &MBoot.sPrtnInfo[i],
1386 PrDisk,
1387 BmInfo)))
[167]1388 return arc;
[8]1389
1390 continue;
1391 }
1392
1393 // raise driver letter if OS/2 would recognize this drive
[108]1394 if ( (MBoot.sPrtnInfo[i].bFileSysCode < 0x75)
[8]1395 )
1396 fAssignLetter = TRUE;
1397
1398 if (fAssignLetter)
1399 (*pcLetter)++;
1400
1401 fBootable = ( (BmInfo)
1402 && ((MBoot.sBmNames[i].bootable & 0x01) != 0)
1403 );
1404
1405 if ((arc = AppendPartition(pppiFirst,
1406 pppiThis,
1407 posCount,
1408 PrDisk,
1409 (fBootable)
1410 ? (char*)&MBoot.sBmNames[i].name
1411 : "",
1412 (fAssignLetter)
1413 ? *pcLetter
1414 : ' ',
1415 MBoot.sPrtnInfo[i].bFileSysCode,
1416 FALSE, // primary
1417 fBootable, // bootable
1418 MBoot.sPrtnInfo[i].lTotalSects)))
[167]1419 return arc;
[8]1420 }
1421 }
1422
[169]1423 return NO_ERROR;
[8]1424}
1425
1426/*
1427 * GetExtendedPartition:
1428 * this finds the extended partition on the given
1429 * drive and calls GetLogicalDrives in turn.
1430 *
1431 * This gets called from doshGetPartitionsList.
1432 *
[108]1433 * Originally contributed by Dmitry A. Steklenev.
[8]1434 *
1435 *@@added V0.9.0 [umoeller]
1436 */
1437
[222]1438STATIC APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
[142]1439 PARTITIONINFO **pppiThis,
1440 PUSHORT posCount,
1441 PCHAR pcLetter,
1442 PAR_INFO* BmInfo,
1443 UINT iDisk) // in: disk to query
[8]1444{
1445 APIRET arc = NO_ERROR;
1446 MBR_INFO MBoot; // Master Boot
1447 USHORT i;
1448
1449 if ((arc = doshReadSector(iDisk, &MBoot, 0, 0, 1)))
[167]1450 return arc;
[8]1451
1452 // go thru MBR entries to find extended partition
1453 for (i = 0;
1454 i < 4;
1455 i++)
1456 {
[108]1457 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
[8]1458 {
1459 if ((arc = GetLogicalDrives(pppiFirst,
1460 pppiThis,
1461 posCount,
1462 pcLetter,
1463 &MBoot.sPrtnInfo[i],
1464 iDisk,
1465 BmInfo)))
[167]1466 return arc;
[8]1467 }
1468 }
1469
[169]1470 return NO_ERROR;
[8]1471}
1472
1473/*
[108]1474 *@@ ReadFDiskPartitions:
1475 * helper for doshGetPartitionsList for non-LVM
1476 * systems.
1477 *
1478 * Originally contributed by Dmitry A. Steklenev.
1479 *
1480 *@@added V0.9.16 (2001-10-08) [umoeller]
1481 */
1482
[222]1483STATIC APIRET ReadFDiskPartitions(PARTITIONINFO **ppPartitionInfos,
[142]1484 USHORT *pcPartitions,
1485 PUSHORT pusContext) // out: error context
[108]1486{
1487 APIRET arc = NO_ERROR;
1488
1489 PAR_INFO BmInfo; // BootManager partition
1490 USHORT usBmDisk; // BootManager disk
1491 USHORT cDisks = doshQueryDiskCount(); // physical disks count
1492 USHORT i;
1493
1494 CHAR cLetter = 'C'; // first drive letter
1495
1496 PARTITIONINFO *ppiTemp = NULL;
1497
1498 if (cDisks > 8) // Not above 8 disks
1499 cDisks = 8;
1500
1501 // get boot manager disk and info
1502 if ((arc = doshGetBootManager(&usBmDisk,
1503 NULL,
1504 &BmInfo)) != NO_ERROR)
1505 {
1506 *pusContext = 1;
1507 }
1508 else
1509 {
1510 // on each disk, read primary partitions
1511 for (i = 1; i <= cDisks; i++)
1512 {
1513 if ((arc = GetPrimaryPartitions(ppPartitionInfos,
1514 &ppiTemp,
1515 pcPartitions,
1516 &cLetter,
1517 usBmDisk,
1518 usBmDisk ? &BmInfo : 0,
1519 i)))
1520 {
1521 *pusContext = 2;
1522 }
1523 }
1524
1525 if (!arc && usBmDisk)
1526 {
1527 // boot manager found:
1528 // on each disk, read extended partition
1529 // with logical drives
1530 for (i = 1; i <= cDisks; i++)
1531 {
1532 if ((arc = GetExtendedPartition(ppPartitionInfos,
1533 &ppiTemp,
1534 pcPartitions,
1535 &cLetter,
1536 &BmInfo,
1537 i)))
1538 {
1539 *pusContext = 3;
1540 }
1541 }
1542 }
1543 } // end else if ((arc = doshGetBootManager(&usBmDisk,
1544
[167]1545 return arc;
[108]1546}
1547
1548#endif
1549
1550/*
[56]1551 *@@ CleanPartitionInfos:
1552 *
1553 *@@added V0.9.9 (2001-04-07) [umoeller]
1554 */
1555
[222]1556STATIC VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
[56]1557{
1558 while (ppiThis)
1559 {
1560 PPARTITIONINFO ppiNext = ppiThis->pNext;
1561 free(ppiThis);
1562 ppiThis = ppiNext;
1563 }
1564}
1565
1566/*
[8]1567 *@@ doshGetPartitionsList:
1568 * this returns lots of information about the
1569 * partitions on all physical disks, which is
1570 * read directly from the MBRs and partition
1571 * tables.
1572 *
1573 * If NO_ERROR is returned by this function,
1574 * *ppPartitionInfo points to a linked list of
1575 * PARTITIONINFO structures, which has
1576 * *pusPartitionCount items.
1577 *
1578 * In that case, use doshFreePartitionsList to
1579 * free the resources allocated by this function.
1580 *
[56]1581 * What this function returns depends on whether
1582 * LVM is installed.
[8]1583 *
[56]1584 * -- If LVM.DLL is found on the LIBPATH, this opens
1585 * the LVM engine and returns the info from the
1586 * LVM engine in the PARTITIONINFO structures.
1587 * The partitions are then sorted by disk in
1588 * ascending order.
1589 *
1590 * -- Otherwise, we parse the partition tables
1591 * manually. The linked list then starts out with
1592 * all the primary partitions, followed by the
1593 * logical drives in the extended partitions.
1594 * This function attempts to guess the correct drive
1595 * letters and stores these with the PARTITIONINFO
1596 * items, but there's no guarantee that this is
1597 * correct. We correctly ignore Linux partitions here
1598 * and give all primary partitions the C: letter, but
1599 * I have no idea what happens with NTFS partitions,
1600 * since I have none.
1601 *
[8]1602 * If an error != NO_ERROR is returned, *pusContext
1603 * will be set to one of the following:
[54]1604 *
[8]1605 * -- 1: boot manager not found
[54]1606 *
[8]1607 * -- 2: primary partitions error
[54]1608 *
[8]1609 * -- 3: secondary partitions error
1610 *
[56]1611 * -- 0: something else.
1612 *
[108]1613 * Originally contributed by Dmitry A. Steklenev.
[8]1614 *
1615 *@@added V0.9.0 [umoeller]
[56]1616 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
[57]1617 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
[8]1618 */
1619
[56]1620APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
[8]1621 PUSHORT pusContext) // out: error context
1622{
1623 APIRET arc = NO_ERROR;
1624
[56]1625 PLVMINFO pLVMInfo = NULL;
1626
[108]1627 PARTITIONINFO *pPartitionInfos = NULL; // linked list of all partitions
[56]1628 USHORT cPartitions = 0; // bootable partition count
[8]1629
[56]1630 if (!ppList)
[238]1631 return ERROR_INVALID_PARAMETER;
[8]1632
[56]1633 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
[8]1634 {
[56]1635 // LVM installed:
1636 arc = doshReadLVMPartitions(pLVMInfo, // in: LVM info
1637 &pPartitionInfos, // out: partitions array
1638 &cPartitions); // out: partitions count
1639 // copied to output below
1640
1641 if (arc)
[8]1642 {
[56]1643 // error: start over
1644 doshFreeLVMInfo(pLVMInfo);
1645 CleanPartitionInfos(pPartitionInfos);
1646 pPartitionInfos = NULL;
1647 cPartitions = 0;
[8]1648 }
[54]1649 }
[8]1650
[108]1651#ifndef __XWPLITE__
[56]1652 if (arc)
1653 // LVM not installed, or failed:
1654 // parse partitions manually
[108]1655 arc = ReadFDiskPartitions(&pPartitionInfos,
1656 &cPartitions,
1657 pusContext);
1658#endif
[56]1659
1660 if (!arc)
1661 {
1662 // no error so far:
1663 *pusContext = 0;
1664
1665 *ppList = NEW(PARTITIONSLIST);
1666 if (!(*ppList))
1667 arc = ERROR_NOT_ENOUGH_MEMORY;
1668 else
1669 {
1670 ZERO(*ppList);
1671
1672 (*ppList)->pPartitionInfo = pPartitionInfos;
1673 (*ppList)->cPartitions = cPartitions;
1674
1675 _Pmpf((__FUNCTION__ ": returning %d partitions", cPartitions));
[8]1676 }
1677 }
1678
[56]1679 if (arc)
1680 CleanPartitionInfos(pPartitionInfos);
[8]1681
[56]1682 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
1683
[167]1684 return arc;
[8]1685}
1686
1687/*
1688 *@@ doshFreePartitionsList:
1689 * this frees the resources allocated by
1690 * doshGetPartitionsList.
1691 *
1692 *@@added V0.9.0 [umoeller]
1693 */
1694
[56]1695APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
[8]1696{
[56]1697 if (!ppList)
[238]1698 return ERROR_INVALID_PARAMETER;
[56]1699
[238]1700 CleanPartitionInfos(ppList->pPartitionInfo);
1701 doshFreeLVMInfo(ppList->pLVMInfo);
1702 free(ppList);
1703
[169]1704 return NO_ERROR;
[8]1705}
1706
[56]1707/********************************************************************
1708 *
1709 * LVM declarations
1710 *
1711 ********************************************************************/
[8]1712
[56]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
[167]2069 return arc;
[56]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)
[238]2096 return ERROR_INVALID_PARAMETER;
[56]2097
2098 // initialize LVM engine
2099 pLVMInfo->Open_LVM_Engine(TRUE,
2100 &Error);
2101
[389]2102 _Pmpf((" Open_LVM_Engine Error: %d", Error));
[56]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
[91]2127 /* Drive_Information_Record pDriveInfoRecord
[56]2128 = pLVMInfo->Get_Drive_Status(hDrive,
2129 &Error);
2130
2131 _Pmpf((" drive %d Get_Drive_Status Error: %d", ulDisk, Error));
2132
[91]2133 if (!Error) */
[56]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 }
[91]2190 /* else
[56]2191 // error:
[91]2192 break; */
[56]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
[167]2220 return arc;
[56]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}
[127]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
[222]2261STATIC BOOL PerformMatch(PCSZ pMask,
[142]2262 PCSZ pName,
2263 int fHasDot)
[127]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
[194]2293 /*
[127]2294 do
2295 {
2296 ++pMask;
2297 } while (*pMask == '*');
[194]2298 */
[127]2299
[194]2300 while (*(++pMask) == '*') // V0.9.20 (2002-07-25) [umoeller]
2301 ;
2302
[127]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/*
[194]2341 *@@ doshMatchCase:
[127]2342 * this matches '*' and '?' wildcards, similar to what
[194]2343 * DosEditName does. However, this does not require a
2344 * file to be present, but works on strings only.
[127]2345 *
[194]2346 * Returns TRUE if the given name matches the given mask.
[127]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 *
[194]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.
[127]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
[135]2373BOOL doshMatchCase(const char *pcszMask, // in: mask (e.g. "*.TXT")
2374 const char *pcszName) // in: string to check (e.g. "TEST.TXT")
[127]2375{
2376 BOOL brc = FALSE;
2377
2378 PCSZ pLastMaskComponent,
2379 pLastNameComponent;
2380
2381 ULONG cbMaskPath = 0,
2382 cbNamePath = 0;
2383
[135]2384 if (pLastMaskComponent = strrchr(pcszMask, '\\'))
[127]2385 {
2386 // length of path component
[135]2387 cbMaskPath = pLastMaskComponent - pcszMask;
[127]2388 pLastMaskComponent++;
2389 }
2390 else
[135]2391 pLastMaskComponent = pcszMask;
[127]2392
[135]2393 if (pLastNameComponent = strrchr(pcszName, '\\'))
[127]2394 {
2395 // length of path component
[135]2396 cbNamePath = pLastNameComponent - pcszName;
[127]2397 pLastNameComponent++;
2398 }
2399 else
[135]2400 pLastNameComponent = pcszName;
[127]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)
[135]2406 || (!memcmp(pcszMask, pcszName, cbMaskPath))
[127]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
[135]2421/*
[194]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/*
[135]2441 *@@ doshMatch:
2442 * like doshMatchCase, but compares without respect
2443 * to case.
2444 *
2445 *@@added V0.9.16 (2002-01-26) [umoeller]
2446 */
[127]2447
[135]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);
[194]2457 nlsUpper(pszMask);
[135]2458 memcpy(pszName, pcszName, cbName + 1);
[194]2459 nlsUpper(pszName);
[135]2460
[194]2461 return doshMatchCase(pszMask,
2462 pszName);
[135]2463}
Note: See TracBrowser for help on using the repository browser.