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

Last change on this file since 422 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
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
489 if ( (!pcszDir)
490 || (!(*pcszDir))
491 )
492 return ERROR_INVALID_PARAMETER;
493
494 if (pcszDir[1] == ':')
495 {
496 // drive given:
497 CHAR cDrive = toupper(*(pcszDir));
498 // change drive
499 arc = DosSetDefaultDisk( (ULONG)(cDrive - 'A' + 1) );
500 // 1 = A:, 2 = B:, ...
501 }
502
503 if (!arc)
504 arc = DosSetCurrentDir((PSZ)pcszDir);
505
506 return arc; // V0.9.9 (2001-04-04) [umoeller]
507}
508
509/*
510 *@@ CopyToBuffer:
511 * little helper for copying a string to
512 * a target buffer with length checking.
513 *
514 * Returns:
515 *
516 * -- NO_ERROR
517 *
518 * -- ERROR_BUFFER_OVERFLOW if pszTarget does
519 * not have enough room to hold pcszSource
520 * (including the null terminator).
521 *
522 *@@added V0.9.16 (2001-10-08) [umoeller]
523 */
524
525STATIC APIRET CopyToBuffer(PSZ pszTarget, // out: target buffer
526 PCSZ pcszSource, // in: source string
527 ULONG cbTarget) // in: size of target buffer
528{
529 ULONG ulLength = strlen(pcszSource);
530 if (ulLength < cbTarget)
531 {
532 memcpy(pszTarget,
533 pcszSource,
534 ulLength + 1);
535 return NO_ERROR;
536 }
537
538 return ERROR_BUFFER_OVERFLOW;
539}
540
541/*
542 *@@ doshSearchPath:
543 * replacement for DosSearchPath.
544 *
545 * This looks along all directories which are
546 * specified in the value of the given environment
547 * variable if pcszFile is found.
548 *
549 * As opposed to the stupid DosSearchPath, this
550 * ignores subdirectories in the path particles.
551 * For example, DosSearchPath would usually not
552 * find an INSTALL file because \OS2 contains
553 * an INSTALL directory, or NETSCAPE because
554 * \OS2\INSTALL contains a NETSCAPE directory.
555 *
556 * Returns:
557 *
558 * -- NO_ERROR: pszExecutable has received the
559 * full path of pcszFile.
560 *
561 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
562 * in the specified path (or is a directory).
563 *
564 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
565 * the pszExecutable buffer is too small to hold
566 * the full path.
567 *
568 *@@added V0.9.16 (2001-10-08) [umoeller]
569 *@@changed V1.0.0 (2002-11-23) [umoeller]: allowing NULL pcszPath to search for "PATH"
570 */
571
572APIRET doshSearchPath(const char *pcszPath, // in: path variable name; if NULL, we use "PATH"
573 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
574 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
575 ULONG cbExecutable) // in: sizeof (*pszExecutable)
576{
577 APIRET arc = NO_ERROR;
578
579 // get the PATH value
580 PCSZ pcszPathValue;
581
582 if (!pcszPath)
583 pcszPath = "PATH"; // V1.0.0 (2002-11-23) [umoeller]
584
585 if (!(arc = DosScanEnv((PSZ)pcszPath,
586#if __cplusplus
587 &pcszPathValue)))
588#else
589 (PSZ*)&pcszPathValue)))
590#endif
591 {
592 // run thru the path components
593 PSZ pszPathCopy;
594 if (pszPathCopy = strdup(pcszPathValue))
595 {
596 PSZ pszToken = strtok(pszPathCopy, ";");
597 while (pszToken)
598 {
599 CHAR szFileMask[2*CCHMAXPATH];
600 FILESTATUS3 fs3;
601
602 sprintf(szFileMask,
603 "%s\\%s",
604 pszToken, // path particle
605 pcszFile); // e.g. "netscape"
606
607 if ( (!(arc = DosQueryPathInfo(szFileMask,
608 FIL_STANDARD,
609 &fs3,
610 sizeof(fs3))))
611 // make sure it's not a directory
612 // and that it's not hidden
613 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
614 )
615 {
616 // copy
617 arc = CopyToBuffer(pszExecutable,
618 szFileMask,
619 cbExecutable);
620 // and stop
621 break;
622 }
623 else
624 arc = ERROR_FILE_NOT_FOUND;
625 // and search on
626
627 pszToken = strtok(NULL, ";");
628 };
629
630 free(pszPathCopy);
631 }
632 else
633 arc = ERROR_NOT_ENOUGH_MEMORY;
634 }
635
636 return arc;
637}
638
639/*
640 *@@ doshSearchDirs:
641 * This looks along all directories which are
642 * specified in the passed directory list
643 * if pcszFile is found.
644 *
645 * As opposed to the stupid DosSearchPath, this
646 * ignores subdirectories in the path particles.
647 * For example, DosSearchPath would usually not
648 * find an INSTALL file because \OS2 contains
649 * an INSTALL directory, or NETSCAPE because
650 * \OS2\INSTALL contains a NETSCAPE directory.
651 *
652 * Returns:
653 *
654 * -- NO_ERROR: pszExecutable has received the
655 * full path of pcszFile.
656 *
657 * -- ERROR_FILE_NOT_FOUND: pcszFile was not found
658 * in the specified path (or is a directory).
659 *
660 * -- ERROR_BUFFER_OVERFLOW: pcszFile was found, but
661 * the pszExecutable buffer is too small to hold
662 * the full path.
663 *
664 *@@added V1.0.4 (2005-06-16) [chennecke]: blatantly stolen from doshSearchPath
665 */
666
667APIRET doshSearchDirs(const char *pcszDirList, // in: list of directories in PATH style
668 const char *pcszFile, // in: file to look for (e.g. "LVM.EXE")
669 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
670 ULONG cbExecutable) // in: sizeof (*pszExecutable)
671{
672 APIRET arc = NO_ERROR;
673
674 // run thru the path components
675 PSZ pszPathCopy;
676 if (pszPathCopy = strdup(pcszDirList))
677 {
678 PSZ pszToken = strtok(pszPathCopy, ";");
679 while (pszToken)
680 {
681 CHAR szFileMask[2*CCHMAXPATH];
682 FILESTATUS3 fs3;
683
684 sprintf(szFileMask,
685 "%s\\%s",
686 pszToken, // path particle
687 pcszFile); // e.g. "netscape"
688
689 if ( (!(arc = DosQueryPathInfo(szFileMask,
690 FIL_STANDARD,
691 &fs3,
692 sizeof(fs3))))
693 // make sure it's not a directory
694 // and that it's not hidden
695 && (!(fs3.attrFile & (FILE_DIRECTORY | FILE_HIDDEN)))
696 )
697 {
698 // copy
699 arc = CopyToBuffer(pszExecutable,
700 szFileMask,
701 cbExecutable);
702 // and stop
703 break;
704 }
705 else
706 arc = ERROR_FILE_NOT_FOUND;
707 // and search on
708
709 pszToken = strtok(NULL, ";");
710 };
711
712 free(pszPathCopy);
713 }
714 else
715 arc = ERROR_NOT_ENOUGH_MEMORY;
716
717 return arc;
718}
719
720/*
721 * FindFile:
722 * helper for doshFindExecutable.
723 *
724 *added V0.9.11 (2001-04-25) [umoeller]
725 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewrote second half for DosSearchPath replacement, which returns directories too
726 */
727
728STATIC APIRET FindFile(const char *pcszCommand, // in: command (e.g. "lvm")
729 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
730 ULONG cbExecutable) // in: sizeof (*pszExecutable)
731{
732 APIRET arc = NO_ERROR;
733 FILESTATUS3 fs3;
734
735 if ( (strchr(pcszCommand, '\\'))
736 || (strchr(pcszCommand, ':'))
737 )
738 {
739 // looks like this is qualified:
740 arc = DosQueryPathInfo((PSZ)pcszCommand,
741 FIL_STANDARD,
742 &fs3,
743 sizeof(fs3));
744 if (!arc)
745 if (!(fs3.attrFile & FILE_DIRECTORY))
746 arc = CopyToBuffer(pszExecutable,
747 pcszCommand,
748 cbExecutable);
749 else
750 // directory:
751 arc = ERROR_INVALID_EXE_SIGNATURE;
752 }
753 else
754 {
755 // non-qualified:
756 /* arc = DosSearchPath(SEARCH_IGNORENETERRS
757 | SEARCH_ENVIRONMENT
758 | SEARCH_CUR_DIRECTORY,
759 "PATH",
760 (PSZ)pcszCommand,
761 pszExecutable,
762 cbExecutable); */
763 // The above is not useable. It returns directories
764 // on the path... for example, it returns \OS2\INSTALL\NETSCAPE
765 // if netscape is looked for. So we search manually... sigh.
766 // V0.9.16 (2001-10-08) [umoeller]
767 arc = doshSearchPath("PATH",
768 pcszCommand,
769 pszExecutable,
770 cbExecutable);
771 }
772
773 return arc;
774}
775
776/*
777 *@@ doshFindExecutable:
778 * this attempts to find an executable by doing the
779 * following:
780 *
781 * 1) If pcszCommand appears to be qualified (i.e. contains
782 * a backslash), this checks for whether the file exists.
783 * If it is a directory, ERROR_INVALID_EXE_SIGNATURE is
784 * returned.
785 *
786 * 2) If pcszCommand contains no backslash, this searches
787 * all directories on the PATH in order to find the full
788 * path of the executable. Starting with V0.9.16, we
789 * use doshSearchPath for that.
790 *
791 * papcszExtensions determines if additional searches are to be
792 * performed if the file doesn't exist (case 1) or doshSearchPath
793 * returned ERROR_FILE_NOT_FOUND (case 2).
794 * This must point to an array of strings specifying the extra
795 * extensions to search for.
796 *
797 * If both papcszExtensions and cExtensions are null, no
798 * extra searches are performed.
799 *
800 * Returns:
801 *
802 * -- NO_ERROR: pszExecutable has received the full path of
803 * the executable found by DosSearchPath.
804 *
805 * -- ERROR_FILE_NOT_FOUND
806 *
807 * -- ERROR_BUFFER_OVERFLOW: pcszCommand was found, but
808 * the pszExecutable buffer is too small to hold
809 * the full path.
810 *
811 * Example:
812 *
813 + const char *aExtensions[] = { "EXE",
814 + "COM",
815 + "CMD"
816 + };
817 + CHAR szExecutable[CCHMAXPATH];
818 + APIRET arc = doshFindExecutable("lvm",
819 + szExecutable,
820 + sizeof(szExecutable),
821 + aExtensions,
822 + 3);
823 *
824 *@@added V0.9.9 (2001-03-07) [umoeller]
825 *@@changed V0.9.11 (2001-04-25) [umoeller]: this never worked for qualified pcszCommand's, fixed
826 */
827
828APIRET doshFindExecutable(const char *pcszCommand, // in: command (e.g. "lvm")
829 PSZ pszExecutable, // out: full path (e.g. "F:\os2\lvm.exe")
830 ULONG cbExecutable, // in: sizeof (*pszExecutable)
831 const char **papcszExtensions, // in: array of extensions (without dots)
832 ULONG cExtensions) // in: array item count
833{
834 APIRET arc = FindFile(pcszCommand,
835 pszExecutable,
836 cbExecutable);
837
838 if ( (arc == ERROR_FILE_NOT_FOUND) // not found?
839 && (cExtensions) // any extra searches wanted?
840 )
841 {
842 // try additional things then
843 PSZ psz2;
844 if (psz2 = (PSZ)malloc(strlen(pcszCommand) + 20))
845 {
846 ULONG ul;
847 for (ul = 0;
848 ul < cExtensions;
849 ul++)
850 {
851 const char *pcszExtThis = papcszExtensions[ul];
852 sprintf(psz2,
853 "%s.%s",
854 pcszCommand,
855 pcszExtThis);
856 arc = FindFile(psz2,
857 pszExecutable,
858 cbExecutable);
859 if (arc != ERROR_FILE_NOT_FOUND)
860 break;
861 }
862
863 free(psz2);
864 }
865 else
866 arc = ERROR_NOT_ENOUGH_MEMORY;
867 }
868
869 return arc;
870}
871
872/*
873 *@@category: Helpers\Control program helpers\Partitions info
874 * functions for retrieving partition information directly
875 * from the partition tables on the disk. See doshGetPartitionsList.
876 */
877
878/********************************************************************
879 *
880 * Partition functions
881 *
882 ********************************************************************/
883
884/*
885 *@@ doshQueryDiskCount:
886 * returns the no. of physical disks installed
887 * on the system.
888 *
889 *@@added V0.9.0 [umoeller]
890 */
891
892UINT doshQueryDiskCount(VOID)
893{
894 USHORT usCount = 0;
895 DosPhysicalDisk(INFO_COUNT_PARTITIONABLE_DISKS, &usCount, 2, 0, 0);
896 return usCount;
897}
898
899/*
900 *@@ doshType2FSName:
901 * this returns a static, zero-terminated string
902 * for the given FS type, or NULL if the type
903 * is unknown.
904 *
905 *@@added V0.9.0 [umoeller]
906 *@@changed V0.9.16 (2001-10-08) [umoeller]: rewritten
907 */
908
909const char* doshType2FSName(unsigned char bFSType) // in: FS type
910{
911 switch (bFSType)
912 {
913 case 0x00: return "empty";
914 case 0x01: return "DOS 12-bit FAT < 10 Mb";
915 case 0x02: return "XENIX root file system";
916 case 0x03: return "XENIX /usr file system (obsolete)";
917 case 0x04: return "DOS 16-bit FAT < 32 Mb";
918 case 0x05: return "DOS 3.3+ extended partition";
919 case 0x06: return "DOS 3.31+ 16-bit FAT > 32 Mb";
920 case 0x07: return "HPFS/NTFS/QNX/Advanced Unix";
921 case 0x08: return "OS/2 1.0-1.3/AIX/Commodore/DELL";
922 case 0x09: return "AIX data/Coherent";
923 case 0x0A: return "OS/2 Boot Manager/OPUS/Coherent Swap";
924 case 0x0B: return "Windows95 with 32-bit FAT";
925 case 0x0C: return "Windows95 with 32-bit FAT (LBA)";
926 case 0x0E: return "Windows 95 VFAT (06h plus LBA)";
927 case 0x0F: return "Windows 95 VFAT (05h plus LBA)";
928 case 0x10: return "OPUS";
929 case 0x11: return "OS/2 Boot Manager hidden 12-bit FAT";
930 case 0x12: return "Compaq Diagnostics";
931 case 0x14: return "OS/2 Boot Manager hidden sub-32M 16-bit FAT";
932 case 0x16: return "OS/2 Boot Manager hidden over-32M 16-bit FAT";
933 case 0x17: return "OS/2 Boot Manager hidden HPFS";
934 case 0x18: return "AST special Windows swap file (\"Zero-Volt Suspend\")";
935 // case 0x21: reserved
936 // case 0x23: reserved
937 case 0x24: return "NEC MS-DOS 3.x";
938 // case 0x26: reserved
939 // case 0x31: reserved
940 // case 0x33: reserved
941 // case 0x34: reserved
942 case 0x35: return "JFS";
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 }
1008
1009 return NULL;
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]
1026 *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed truncated LVM names
1027 */
1028
1029STATIC APIRET AppendPartition(PARTITIONINFO **pppiFirst,
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
1039{
1040 APIRET arc = NO_ERROR;
1041 PPARTITIONINFO ppiNew;
1042 if (ppiNew = NEW(PARTITIONINFO))
1043 {
1044 ZERO(ppiNew);
1045
1046 // store data
1047 ppiNew->bDisk = bDisk;
1048 if ( (fBootable)
1049 && (pszBootName)
1050 )
1051 {
1052 // fixed truncated LVM names V0.9.20 (2002-08-10) [umoeller]
1053 strhncpy0(ppiNew->szBootName,
1054 pszBootName,
1055 sizeof(ppiNew->szBootName));
1056 }
1057 else
1058 ppiNew->szBootName[0] = 0;
1059 ppiNew->cLetter = cLetter;
1060 ppiNew->bFSType = bFsType;
1061 ppiNew->pcszFSType = doshType2FSName(bFsType);
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
1086 return arc;
1087}
1088
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
1137 return arc;
1138}
1139
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 *
1155 * Originally contributed by Dmitry A. Steklenev.
1156 *
1157 *@@added V0.9.0 [umoeller]
1158 */
1159
1160STATIC USHORT GetCyl(USHORT rBeginSecCyl)
1161{
1162 return ((rBeginSecCyl & 0x00C0) << 2)
1163 + ((rBeginSecCyl & 0xFF00) >> 8);
1164}
1165
1166/*
1167 * GetSec:
1168 * get sector number.
1169 *
1170 * Originally contributed by Dmitry A. Steklenev.
1171 *
1172 *@@added V0.9.0 [umoeller]
1173 */
1174
1175STATIC USHORT GetSec(USHORT rBeginSecCyl)
1176{
1177 return rBeginSecCyl & 0x003F;
1178}
1179
1180/*
1181 *@@ doshGetBootManager:
1182 * this goes thru the master boot records on all
1183 * disks to find the boot manager partition.
1184 *
1185 * Returns:
1186 *
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.
1192 *
1193 * -- ERROR_NOT_SUPPORTED (50): boot manager not installed.
1194 *
1195 * Originally contributed by Dmitry A. Steklenev.
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)
1202 PAR_INFO *pBmInfo) // out: if != NULL, boot manager partition info
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
1218 if ((arc = doshReadSector(usDisk,
1219 &MBoot,
1220 0, // head
1221 0, // cylinder
1222 1))) // sector
1223 return arc;
1224
1225 // scan primary partitions for whether
1226 // BootManager partition exists
1227 for (usPrim = 0; usPrim < 4; usPrim++)
1228 {
1229 if (MBoot.sPrtnInfo[usPrim].bFileSysCode == 0x0A)
1230 {
1231 // this is boot manager:
1232 if (pBmInfo)
1233 *pBmInfo = MBoot.sPrtnInfo[usPrim];
1234 if (pusPart)
1235 *pusPart = usPrim;
1236 if (pusDisk)
1237 *pusDisk = usDisk;
1238 // stop scanning
1239 return NO_ERROR;
1240 }
1241 }
1242 }
1243
1244 return ERROR_NOT_SUPPORTED;
1245}
1246
1247/*
1248 * GetPrimaryPartitions:
1249 * this returns the primary partitions.
1250 *
1251 * This gets called from doshGetPartitionsList.
1252 *
1253 * Returns:
1254 *
1255 * -- ERROR_INVALID_PARAMETER: BMInfo is NULL.
1256 *
1257 * Originally contributed by Dmitry A. Steklenev.
1258 *
1259 *@@added V0.9.0 [umoeller]
1260 */
1261
1262STATIC APIRET GetPrimaryPartitions(PARTITIONINFO **pppiFirst,
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
1269{
1270 APIRET arc = NO_ERROR;
1271
1272 if (!pBmInfo)
1273 arc = ERROR_INVALID_PARAMETER;
1274 else
1275 {
1276 SYS_INFO MName[32]; // Name Space from Boot Manager
1277 memset(&MName, 0, sizeof(MName));
1278
1279 // read boot manager name table;
1280 // this is in the boot manager primary partition
1281 // at sector offset 3 (?!?)
1282 if (!(arc = doshReadSector(BmDisk,
1283 &MName,
1284 // head, cylinder, sector of bmgr primary partition:
1285 pBmInfo->bBeginHead,
1286 GetCyl(pBmInfo->rBeginSecCyl),
1287 GetSec(pBmInfo->rBeginSecCyl) + 3)))
1288 {
1289 // got bmgr name table:
1290 MBR_INFO MBoot; // Master Boot
1291 USHORT i;
1292
1293 // read master boot record of this disk
1294 if (!(arc = doshReadSector(iDisk,
1295 &MBoot,
1296 0, // head
1297 0, // cylinder
1298 1))) // sector
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
1306 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A) // skip boot manager
1307 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x05) // skip extended partition
1308 )
1309 {
1310 BOOL fBootable = ( (pBmInfo)
1311 && (MName[(iDisk-1) * 4 + i].bootable & 0x01)
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)))
1326 return arc;
1327 }
1328 }
1329 }
1330 }
1331 }
1332
1333 return arc;
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 *
1344 * Originally contributed by Dmitry A. Steklenev.
1345 *
1346 *@@added V0.9.0 [umoeller]
1347 */
1348
1349STATIC APIRET GetLogicalDrives(PARTITIONINFO **pppiFirst,
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)
1356{
1357 APIRET arc = NO_ERROR;
1358 EXT_INFO MBoot; // Master Boot
1359 USHORT i;
1360
1361 if ((arc = doshReadSector(PrDisk,
1362 &MBoot,
1363 PrInfo->bBeginHead,
1364 GetCyl(PrInfo->rBeginSecCyl),
1365 GetSec(PrInfo->rBeginSecCyl))))
1366 return arc;
1367
1368 for (i = 0; i < 4; i++)
1369 {
1370 // skip unused partition or BootManager partition
1371 if ( (MBoot.sPrtnInfo[i].bFileSysCode)
1372 && (MBoot.sPrtnInfo[i].bFileSysCode != 0x0A)
1373 )
1374 {
1375 BOOL fBootable = FALSE;
1376 BOOL fAssignLetter = FALSE;
1377
1378 // special work around extended partition
1379 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1380 {
1381 if ((arc = GetLogicalDrives(pppiFirst,
1382 pppiThis,
1383 posCount,
1384 pcLetter,
1385 &MBoot.sPrtnInfo[i],
1386 PrDisk,
1387 BmInfo)))
1388 return arc;
1389
1390 continue;
1391 }
1392
1393 // raise driver letter if OS/2 would recognize this drive
1394 if ( (MBoot.sPrtnInfo[i].bFileSysCode < 0x75)
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)))
1419 return arc;
1420 }
1421 }
1422
1423 return NO_ERROR;
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 *
1433 * Originally contributed by Dmitry A. Steklenev.
1434 *
1435 *@@added V0.9.0 [umoeller]
1436 */
1437
1438STATIC APIRET GetExtendedPartition(PARTITIONINFO **pppiFirst,
1439 PARTITIONINFO **pppiThis,
1440 PUSHORT posCount,
1441 PCHAR pcLetter,
1442 PAR_INFO* BmInfo,
1443 UINT iDisk) // in: disk to query
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)))
1450 return arc;
1451
1452 // go thru MBR entries to find extended partition
1453 for (i = 0;
1454 i < 4;
1455 i++)
1456 {
1457 if (MBoot.sPrtnInfo[i].bFileSysCode == 0x05)
1458 {
1459 if ((arc = GetLogicalDrives(pppiFirst,
1460 pppiThis,
1461 posCount,
1462 pcLetter,
1463 &MBoot.sPrtnInfo[i],
1464 iDisk,
1465 BmInfo)))
1466 return arc;
1467 }
1468 }
1469
1470 return NO_ERROR;
1471}
1472
1473/*
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
1483STATIC APIRET ReadFDiskPartitions(PARTITIONINFO **ppPartitionInfos,
1484 USHORT *pcPartitions,
1485 PUSHORT pusContext) // out: error context
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
1545 return arc;
1546}
1547
1548#endif
1549
1550/*
1551 *@@ CleanPartitionInfos:
1552 *
1553 *@@added V0.9.9 (2001-04-07) [umoeller]
1554 */
1555
1556STATIC VOID CleanPartitionInfos(PPARTITIONINFO ppiThis)
1557{
1558 while (ppiThis)
1559 {
1560 PPARTITIONINFO ppiNext = ppiThis->pNext;
1561 free(ppiThis);
1562 ppiThis = ppiNext;
1563 }
1564}
1565
1566/*
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 *
1581 * What this function returns depends on whether
1582 * LVM is installed.
1583 *
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 *
1602 * If an error != NO_ERROR is returned, *pusContext
1603 * will be set to one of the following:
1604 *
1605 * -- 1: boot manager not found
1606 *
1607 * -- 2: primary partitions error
1608 *
1609 * -- 3: secondary partitions error
1610 *
1611 * -- 0: something else.
1612 *
1613 * Originally contributed by Dmitry A. Steklenev.
1614 *
1615 *@@added V0.9.0 [umoeller]
1616 *@@changed V0.9.9 (2001-04-07) [umoeller]: added transparent LVM support; changed prototype
1617 *@@changed V0.9.9 (2001-04-07) [umoeller]: fixed memory leaks on errors
1618 */
1619
1620APIRET doshGetPartitionsList(PPARTITIONSLIST *ppList,
1621 PUSHORT pusContext) // out: error context
1622{
1623 APIRET arc = NO_ERROR;
1624
1625 PLVMINFO pLVMInfo = NULL;
1626
1627 PARTITIONINFO *pPartitionInfos = NULL; // linked list of all partitions
1628 USHORT cPartitions = 0; // bootable partition count
1629
1630 if (!ppList)
1631 return ERROR_INVALID_PARAMETER;
1632
1633 if (!(arc = doshQueryLVMInfo(&pLVMInfo)))
1634 {
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)
1642 {
1643 // error: start over
1644 doshFreeLVMInfo(pLVMInfo);
1645 CleanPartitionInfos(pPartitionInfos);
1646 pPartitionInfos = NULL;
1647 cPartitions = 0;
1648 }
1649 }
1650
1651#ifndef __XWPLITE__
1652 if (arc)
1653 // LVM not installed, or failed:
1654 // parse partitions manually
1655 arc = ReadFDiskPartitions(&pPartitionInfos,
1656 &cPartitions,
1657 pusContext);
1658#endif
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));
1676 }
1677 }
1678
1679 if (arc)
1680 CleanPartitionInfos(pPartitionInfos);
1681
1682 _Pmpf((__FUNCTION__ ": exiting, arc = %d", arc));
1683
1684 return arc;
1685}
1686
1687/*
1688 *@@ doshFreePartitionsList:
1689 * this frees the resources allocated by
1690 * doshGetPartitionsList.
1691 *
1692 *@@added V0.9.0 [umoeller]
1693 */
1694
1695APIRET doshFreePartitionsList(PPARTITIONSLIST ppList)
1696{
1697 if (!ppList)
1698 return ERROR_INVALID_PARAMETER;
1699
1700 CleanPartitionInfos(ppList->pPartitionInfo);
1701 doshFreeLVMInfo(ppList->pLVMInfo);
1702 free(ppList);
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.