mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1862 lines
62 KiB
1862 lines
62 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
name.c
|
|
|
|
Abstract:
|
|
|
|
This module implements OS/2 V2.0 name processing
|
|
|
|
Author:
|
|
|
|
Therese Stowell (thereses) 26-Sep-1989
|
|
|
|
Revision History:
|
|
|
|
Yaron Shamir (YaronS) 7-June-91
|
|
Move Named Pipes from \OS2SS\DRIVES\NamedPipe to
|
|
\OS2SS\PIPE
|
|
|
|
Yaron Shamir (YaronS) 26-Aug-91
|
|
Support UNC. Link to \OS2SS\UNC
|
|
|
|
Beni Lavi (BeniL) 4-Mar-92
|
|
Support MAILSLOT. Link to \OS2SS\MAILSLOT
|
|
|
|
--*/
|
|
|
|
#define INCL_OS2V20_MEMORY
|
|
#define INCL_OS2V20_QUEUES
|
|
#define INCL_OS2V20_SEMAPHORES
|
|
#define INCL_OS2V20_ERRORS
|
|
#include "os2dll.h"
|
|
#ifdef DBCS
|
|
// MSKK Oct.20.1993 V-AkihiS
|
|
#include "conrqust.h"
|
|
#include "os2win.h"
|
|
#endif
|
|
|
|
APIRET
|
|
VerifyDriveExists(
|
|
IN ULONG DiskNumber
|
|
);
|
|
|
|
#ifdef DBCS
|
|
// MSKK Oct.27.1993 V-Akihis
|
|
CHAR Od2InvalidCharacters[]= { '"',
|
|
'/',
|
|
(CHAR)OBJ_NAME_PATH_SEPARATOR,
|
|
':',
|
|
'<',
|
|
'|',
|
|
'>',
|
|
'\0'
|
|
};
|
|
|
|
CHAR Od2FatInvalidCharacters[]= { '"',
|
|
'/',
|
|
(CHAR)OBJ_NAME_PATH_SEPARATOR,
|
|
'[',
|
|
']',
|
|
':',
|
|
'<',
|
|
'|',
|
|
'>',
|
|
'+',
|
|
'=',
|
|
';',
|
|
',',
|
|
'\0'
|
|
};
|
|
#else
|
|
CHAR Od2InvalidCharacters[]= { '"',
|
|
'/',
|
|
(CHAR)OBJ_NAME_PATH_SEPARATOR,
|
|
'[',
|
|
']',
|
|
':',
|
|
'<',
|
|
'|',
|
|
'>',
|
|
'+',
|
|
'=',
|
|
';',
|
|
',',
|
|
'\0'
|
|
};
|
|
#endif
|
|
|
|
//character devices:
|
|
// emulate existence of "default" devices
|
|
// maintain list of installed devices. symbolic link to real name.
|
|
//
|
|
//question: what if device exists not in \DEV\ directory and process opens it?
|
|
//
|
|
//name processing
|
|
//
|
|
// - map all /s to \s
|
|
// - process . and ..s
|
|
// - prepend current directory to relative paths
|
|
// - remove trailing blanks and dots
|
|
// - detect UNC. compact multiple leading \s
|
|
// - compact multiple \s
|
|
// - detect pipe. disallow leading d:
|
|
// - detect installed and "default" devices
|
|
// - detect illegal chars?
|
|
//
|
|
//
|
|
// detect device (in list of default/installed devices)
|
|
// detect UNC (multiple leading \\s)
|
|
// detect PIPE (leading \PIPE\)
|
|
// if !(UNC || PIPE) && relative path
|
|
// prepend current directory
|
|
// process . and ..s
|
|
// map all /s to \s
|
|
// remove trailing blanks and dots
|
|
//
|
|
//
|
|
|
|
BOOLEAN
|
|
TestForPipe(
|
|
IN PSZ name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether a filename is a pipe name.
|
|
|
|
Arguments:
|
|
|
|
name - filename to test
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the filename is a pipe name. FALSE
|
|
otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
while (ISSLASH(*name)) { // eat any extra slashes
|
|
name++;
|
|
}
|
|
if (_strnicmp(name,"pipe",4)) {
|
|
return FALSE;
|
|
}
|
|
if (!(ISSLASH(*(name+4)))) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
TestForMailslot(
|
|
IN PSZ name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether a filename is a mailslot name.
|
|
|
|
Arguments:
|
|
|
|
name - filename to test
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the filename is a mailslot name. FALSE
|
|
otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
while (ISSLASH(*name)) { // eat any extra slashes
|
|
name++;
|
|
}
|
|
if (_strnicmp(name,"mailslot",8)) {
|
|
return FALSE;
|
|
}
|
|
if (!(ISSLASH(*(name+8)))) {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
PSZ
|
|
FindLastComponent(
|
|
IN PSZ String
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the last component in a string. the string has not
|
|
been canonicalized.
|
|
|
|
Arguments:
|
|
|
|
String - string to examine
|
|
|
|
Return Value:
|
|
|
|
pointer to last component
|
|
|
|
--*/
|
|
|
|
{
|
|
PSZ LastComponent;
|
|
|
|
#ifdef DBCS
|
|
// MSKK Apr.11.1993 V-AkihiS
|
|
if ((!Ow2NlsIsDBCSLeadByte(String[COLON-1], SesGrp->DosCP)) && String[COLON] == ':') {
|
|
#else
|
|
if (String[COLON] == ':') {
|
|
#endif
|
|
LastComponent = String+FIRST_SLASH;
|
|
}
|
|
else {
|
|
LastComponent = String;
|
|
}
|
|
while (*String) {
|
|
#ifdef DBCS
|
|
// MSKK Apr.11.1993 V-AkihiS
|
|
if (Ow2NlsIsDBCSLeadByte(*String, SesGrp->DosCP)) {
|
|
String++;
|
|
if (*String) {
|
|
String++;
|
|
}
|
|
}
|
|
else {
|
|
if (ISSLASH(*String))
|
|
LastComponent = String;
|
|
String++;
|
|
}
|
|
#else
|
|
if (ISSLASH(*String))
|
|
LastComponent = String;
|
|
String++; // BUGBUG make dbcs-correct
|
|
#endif
|
|
}
|
|
if (ISSLASH(*LastComponent))
|
|
return LastComponent+1;
|
|
else
|
|
return LastComponent;
|
|
}
|
|
|
|
/*
|
|
;*** DOSICANONICALIZE - Convert a path name into a standard format
|
|
;
|
|
; INTERNAL ONLY API. Used by a couple of dynamic link libraries.
|
|
; The worker routine does NO error checking or memory protection
|
|
; checks. It is possible to general an internal error if used
|
|
; incorrectly. DO NOT PUBLISH.
|
|
;
|
|
; INVOKE PUSH@ ASCIIZ Source (2 words)
|
|
; PUSH@ ASCIIZ Dest (2 words)
|
|
; PUSH WORD BackupOffset (1 word)
|
|
; PUSH WORD DestEnd (1 word)
|
|
; PUSH WORD Flags (1 word)
|
|
; call DOSICANONICALIZE
|
|
;
|
|
; RETURN (ax) = error code
|
|
;
|
|
*/
|
|
void DosICanonicalize(PCHAR Source, PCHAR Dest, USHORT BackupOffset,
|
|
USHORT DestEnd, USHORT Flags)
|
|
{
|
|
PCHAR SourcePtr,DestPtr;
|
|
char Buffer[256];
|
|
STRING tmpstr;
|
|
|
|
tmpstr.Length=0;
|
|
tmpstr.MaximumLength=256;
|
|
tmpstr.Buffer=Buffer;
|
|
|
|
SourcePtr = Source + BackupOffset;
|
|
DestPtr = Dest + BackupOffset;
|
|
|
|
Od2Canonicalize(
|
|
SourcePtr,
|
|
CANONICALIZE_FILE_DEV_OR_PIPE,
|
|
&tmpstr,
|
|
NULL, // OUT PHANDLE DirectoryHandle OPTIONAL,
|
|
NULL, // OUT PULONG ParseFlags OPTIONAL,
|
|
NULL // OUT PULONG FileType OPTIONAL
|
|
);
|
|
strncpy(DestPtr,tmpstr.Buffer,DestEnd-BackupOffset);
|
|
}
|
|
|
|
APIRET
|
|
Od2Canonicalize(
|
|
IN PSZ Name,
|
|
IN ULONG ExpectedType,
|
|
OUT PSTRING CanonicalString,
|
|
OUT PHANDLE DirectoryHandle OPTIONAL,
|
|
OUT PULONG ParseFlags OPTIONAL,
|
|
OUT PULONG FileType OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine canonicalizes a filename and determines its type, based
|
|
on naming conventions.
|
|
|
|
Arguments:
|
|
|
|
Name - Supplies a pointer to a null terminated path name that will be
|
|
parsed.
|
|
|
|
ExpectedType - Supplies the expected type of the path string that will be
|
|
parsed. Any of the following:
|
|
|
|
CANONICALIZE_FILE_DEV_OR_PIPE - the input path must refer to either
|
|
a local file or device name, a UNC file name, or a named pipe.
|
|
|
|
CANONICALIZE_FILE_OR_DEV - the input path must refer to either
|
|
a local file or device name, or a UNC file name. Named pipes
|
|
are treated as local files. Used by DosMove for renaming
|
|
files that begin with \pipe
|
|
|
|
CANONICALIZE_SHARED_MEMORY - the input path must refer to a shared
|
|
memory section. This means it must begin with \sharemem\
|
|
|
|
CANONICALIZE_SEMAPHORE - the input path must refer to a 32-bit
|
|
semaphore name. This means it must begin with \sem32\
|
|
|
|
CANONICALIZE_QUEUE - the input path must refer to a 32-bit queue
|
|
name. This means it must begin with \queues\
|
|
|
|
CANONICALIZE_MAILSLOT - the input path must refer to a mailslot
|
|
name. This means it must begin with \mailslot\ or \\*\mailslot\
|
|
|
|
CanonicalString - Supplies a pointer to a counted string that will be
|
|
set to point to a buffer containing the result of canonicalizing
|
|
the input path string. The space for the buffer is allocated
|
|
from the heap specified by the OutputHeap parameter or Od2Heap
|
|
if that parameter is not specified.
|
|
|
|
DirectoryHandle - Supplies an optional pointer to a place to store an
|
|
NT Directory handle if the output string can be expressed as a
|
|
relative path string, relative to a current directory. If this
|
|
parameter is not specified or if the returned directory handle is
|
|
NULL then the canonical path string placed in OutputString will
|
|
be a fully qualified path string.
|
|
|
|
ParseFlags - Supplies an optional pointer to flags will be used to
|
|
return information about the results of the parse.
|
|
|
|
This parameter is ignored if the ExpectedType parameter is
|
|
anything other than CANONICALIZE_FILE_DEV_OR_PIPE or
|
|
CANONICALIZE_FILE_OR_DEV.
|
|
|
|
Flag values set by this function:
|
|
|
|
CANONICALIZE_META_CHARS_FOUND - the path name contains either an
|
|
asterisk (*) and/or question marks (?) in the last component
|
|
of the output string.
|
|
|
|
CANONICALIZE_IS_ROOT_DIRECTORY - the output string specifies a root
|
|
directory (e.g. D:\).
|
|
|
|
FileType - Supplies an optional pointer to a place to store the type
|
|
of object the canonical path string describes. Possible values
|
|
are:
|
|
|
|
FILE_TYPE_FILE
|
|
FILE_TYPE_NMPIPE
|
|
FILE_TYPE_DEV
|
|
FILE_TYPE_PSDEV
|
|
FILE_TYPE_MAILSLOT
|
|
|
|
This parameter is ignored if the ExpectedType parameter is
|
|
anything other than CANONICALIZE_FILE_DEV_OR_PIPE,
|
|
CANONICALIZE_FILE_OR_DEV or CANONICALIZE_MAILSLOT.
|
|
|
|
Return Value:
|
|
|
|
OS/2 Error Code
|
|
|
|
Return Value:
|
|
|
|
ERROR_FILE_NOT_FOUND - the filename is invalid.
|
|
|
|
Note:
|
|
|
|
This routine allocates a buffer to hold the canonicalized name. it
|
|
is the caller's responsibility to free this buffer.
|
|
|
|
Pseudocode:
|
|
determine expected prefix
|
|
if (file_pipe_or_dev)
|
|
detect UNC - insert correct prefix
|
|
detect devices - insert correct prefix
|
|
insert drive letter if needed - insert correct prefix
|
|
insert current directory if needed
|
|
else
|
|
check for ///prefix/
|
|
insert correct prefix
|
|
copy string, removing . and .., checking for illegal characters,
|
|
detecting metacharacters
|
|
|
|
Routine Description:
|
|
|
|
This function performs the OS/2 V2.0 Path Name canonicalization
|
|
function. It takes as input a pointer to a null terminated string
|
|
that is considered unprobed, along with flags that specify optional
|
|
behavior while parsing the path string. The output of this function
|
|
is a canonical path string that is in NT format. The OS/2 Subsystem
|
|
creates the following objects in the NT Object Name Space in order
|
|
to support the output of this function:
|
|
|
|
\OS2SS object directory
|
|
\OS2SS\DRIVES object directory
|
|
\OS2SS\DRIVES\A: symbolic link => \Device\Floppy0
|
|
\OS2SS\DRIVES\C: symbolic link => \Device\Harddisk0\Partition1
|
|
\OS2SS\DRIVES\D: symbolic link => \Device\Harddisk1\Partition1
|
|
\OS2SS\PIPE symbolic link => \DosDevices\PIPE
|
|
\OS2SS\UNC symbolic link => \DosDevices\UNC
|
|
\OS2SS\DEVICES object directory
|
|
\OS2SS\DEVICES\NUL symbolic link => @0
|
|
\OS2SS\DEVICES\CON symbolic link => @1
|
|
\OS2SS\DEVICES\AUX symbolic link => @2
|
|
\OS2SS\DEVICES\COM1 symbolic link => @2.0
|
|
\OS2SS\DEVICES\COM2 symbolic link => @2.1
|
|
\OS2SS\DEVICES\COM3 symbolic link => @2.2
|
|
\OS2SS\DEVICES\COM4 symbolic link => @2.3
|
|
\OS2SS\DEVICES\PRN symbolic link => @3
|
|
\OS2SS\DEVICES\LPT1 symbolic link => @3.0
|
|
\OS2SS\DEVICES\LPT2 symbolic link => @3.1
|
|
\OS2SS\DEVICES\LPT3 symbolic link => @3.2
|
|
\OS2SS\DEVICES\KBD$ symbolic link => @4
|
|
\OS2SS\DEVICES\MOUSE$ symbolic link => @5
|
|
\OS2SS\DEVICES\CLOCK$ symbolic link => @6
|
|
\OS2SS\DEVICES\SCREEN$ symbolic link => @7
|
|
\OS2SS\DEVICES\POINTER$ symbolic link => @8
|
|
\OS2SS\QUEUES object directory
|
|
\OS2SS\SHAREMEM object directory
|
|
\OS2SS\SEMAPHORES object directory
|
|
|
|
The actual drive letters that will be defined in the \OS2SS\DRIVES
|
|
object directory actually depend upon the hardware configuration you
|
|
are running on. The above is an example for a particular machine.
|
|
Also notice that the GlobalDFS link actually is the same as the
|
|
LocalDFS link, namely the redirector. This will be true until we
|
|
implement a real name service file system.
|
|
|
|
Listed below are some examples of OS/2 path strings and the desired
|
|
NT path string. These examples assume the current drive and
|
|
directory for the OS/2 application are C:\Os2\Dll
|
|
|
|
OS/2 Path NT Path
|
|
|
|
pmwin.dll \OS2SS\DRIVES\C:\Os2\Dll\pmwin.dll
|
|
c:pmwin.dll \OS2SS\DRIVES\C:\Os2\Dll\pmwin.dll
|
|
C:\os2\dll\pmwin.dll \OS2SS\DRIVES\C:\os2\dll\pmwin.dll
|
|
c:\DLL\..\pmwin.dll \OS2SS\DRIVES\C:\Os2\DLL\pmwin.dll
|
|
c:\OS2\.\DLL\..\..\pmwin.dll \OS2SS\DRIVES\C:\OS2\DLL\pmwin.dll
|
|
\\mach\shr\a\b\c \OS2SS\DRIVES\LocalDFS\mach\shr\a\b\c
|
|
\\\mach\shr\a\b\c \OS2SS\DRIVES\GlobalDFS\mach\shr\a\b\c
|
|
\pipe\a\b\c\pipe.C \OS2SS\DRIVES\PIPE/a/b/c/pipe.C
|
|
con @1 - internal pseudo device
|
|
c:clock$ @6 - internal pseudo device
|
|
\a\b\c\screen$ @7 - internal pseudo device
|
|
\queUES\a\b\c\..\..\..\que. \OS2SS\QUEUES\QUE
|
|
\shaREmem\a\b\c\.\mem.c \OS2SS\SHAREDMEMORY\A/B/C/MEM.C
|
|
\sem32\a\b\c\..\sem.b \OS2SS\SEMAPHORES\A/B/SEM.B
|
|
|
|
Notice that for the last three examples, the output string contains
|
|
forward slashes instead of the normal backslash path separator.
|
|
This allows the names to be stored in the NT Object Name Space
|
|
without having to create the implied directory structure. The
|
|
NamedPipes example also does this, although this can be changed
|
|
if the NamedPipe File System is integrated into the NT Pinball File
|
|
System.
|
|
|
|
Also for the first five examples, since each NT path begins with the
|
|
current drive and directory, the output path returned by this function
|
|
will be just the file name, pmwin.dll and the NT handle that points
|
|
to the current directory of C: (i.e. \OS2SS\DRIVES\C:\Os2\Dll). This
|
|
allows cheaper relative opens to be used for most opens of files.
|
|
|
|
What follows is a description of how this function parses the input
|
|
path string:
|
|
|
|
- first allocates a buffer from Od2Heap. The buffer is large
|
|
enough to contain the longest possible OS/2 path string, plus
|
|
the space implied by the length of the input path string.
|
|
|
|
- figures out what prefix to use based on expected file type.
|
|
|
|
- if a file system path is expected (file, pipe, unc, or device),
|
|
determines what type of path is input. the correct constant prefix is
|
|
copied to the beginning of the buffer. A UNC path is first detected
|
|
by checking for two leading slashes. If "\\" is found,
|
|
"\OS2SS\UNC" is copied to the buffer.
|
|
Then the server name is copied to the buffer. The end of the server name is the backup limit for
|
|
'..' processing. Then a pipe path is checked for by looking for the
|
|
"PIPE" prefix at the beginning of the path. If it is found,
|
|
"\OS2SS\PIPE" is copied into the buffer. Also
|
|
set a flag so that all backslash path separator characters in the
|
|
output string will be mapped to forward slashes. The end of the
|
|
\pipe\ prefix is the backup limit for ".." processing. If the
|
|
path is not UNC or pipe, a device path is checked for by doing the
|
|
following:
|
|
|
|
lookup the trailing component of the output path string in
|
|
the \OS2SS\DEVICES directory as a symbolic link. If found
|
|
then extract the target string of the symbolic link and
|
|
parse it as follows:
|
|
|
|
@n[.m] - specifies a pseudo-device that is builtin to
|
|
the OS/2 Emulation subsystem. n is a decimal, zero
|
|
based index into the pseudo-device table. The ".m" is
|
|
optional and if present, m is a decimal, zero based
|
|
number that represents the unit number. For devices
|
|
with unit numbers, if m is not present a default unit
|
|
number should be used (zero in most cases). In either
|
|
case the type returned is FILE_TYPE_PSDEV. The caller
|
|
should key off of this type to determine if it should
|
|
parse the contents of the canonical name string.
|
|
|
|
NT Path String - specifies a fully qualified name of a
|
|
physical device. The type returned is FILE_TYPE_DEV
|
|
|
|
If the path is not a device, it is assumed to be a file. The
|
|
drive letter and current directory are copied to buffer if needed.
|
|
The buffer will contain \OS2SS\DRIVES\d:\currentdir at this point.
|
|
The slash after the drive letter is the backup limit for ".."
|
|
processing.
|
|
|
|
The filetype is set according to the type of path found.
|
|
|
|
- if a non-file system path is expected, the expected prefix is
|
|
verified and the constant and expected prefixes are copied to the
|
|
buffer.
|
|
|
|
if the ExpectedType parameter is CANONICALIZE_QUEUES, then see
|
|
if the beginning of the input path string matches \QUEUES\
|
|
ignoring case. If they do not match, then the
|
|
ERROR_PATH_NOT_FOUND error code is returned from this
|
|
function. Also set a flag so that all backslash path
|
|
separator characters in the output string will be mapped
|
|
to forward slashes. If it does match then \OS2SS\QUEUES\QUEUES
|
|
is copied to the buffer. The end of the second QUEUES prefix
|
|
is the backup limit for ".." processing.
|
|
|
|
if the ExpectedType parameter is CANONICALIZE_SEMAPHORE, then
|
|
see if the beginning of the input path string matches \SEM32\
|
|
ignoring case. If they do not match, then the
|
|
ERROR_PATH_NOT_FOUND error code is returned from this
|
|
function. Also set a flag so that all backslash path
|
|
separator characters in the output string will be mapped to
|
|
forward slashes. If it does match then \OS2SS\SEMAPHORES\SEM32
|
|
is copied to the buffer. The end of the SEM32 prefix
|
|
is the backup limit for ".." processing.
|
|
|
|
if the ExpectedType parameter is CANONICALIZE_SHARED_MEMORY,
|
|
then see if the beginning of the input path string matches
|
|
\SHAREMEM\ ignoring case. If they do not match, then the
|
|
ERROR_PATH_NOT_FOUND error code is returned from this
|
|
function. Also set a flag so that all backslash path
|
|
separator characters in the output string will be mapped
|
|
to forward slashes. If it does match then
|
|
\OS2SS\SHAREDMEMORY\SHAREMEM is copied to the buffer. The end of
|
|
the SHAREMEM prefix is the backup limit for ".." processing.
|
|
|
|
- copies the unprobed remainder of the input path string to the
|
|
allocated buffer, performing the following logic for each character:
|
|
|
|
- if an access violation exception occurs when fetching a
|
|
character from the input path string, the ERROR_INVALID_ADDRESS
|
|
error code is returned as the value of this function.
|
|
|
|
- forward slash (/) path separators into are converted to the
|
|
NT path separator character (OBJ_NAME_PATH_SEPARATOR).
|
|
|
|
- meta characters (* and ?) are detected. If found before a
|
|
path separator then the ERROR_PATH_NOT_FOUND error code is
|
|
returned as the value of this function.
|
|
|
|
- if the ExpectedType parameter is neither of
|
|
CANONICALIZE_FILE_DEV_OR_PIPE or CANONICALIZE_FILE_OR_DEV,
|
|
then lower case letters are converted to upper case when
|
|
they are copied to the output buffer.
|
|
|
|
- if the ExpectedType parameter is neither of
|
|
CANONICALIZE_FILE_DEV_OR_PIPE or CANONICALIZE_FILE_OR_DEV and
|
|
meta characters are detected then the ERROR_PATH_NOT_FOUND error
|
|
code is returned as the value of this function.
|
|
|
|
- if the input string begins with a character followed by a
|
|
colon (:), then map the drive letter to upper case.
|
|
|
|
- sequences one or more adjacent backslashes
|
|
(OBJ_NAME_PATH_SEPARATOR) are converted to a single backslash.
|
|
|
|
- process '.' and '..' path components. '.' are removed. '..'
|
|
components cause the immediately previous component to be
|
|
removed. i.e. d:\foo\..\bar -> d:\bar. An error is returned
|
|
if there is no component to remove.
|
|
|
|
- If specified by the earlier logic, the trailing path separator
|
|
of each component is set to be a forward slash after the copy.
|
|
This basically maps any multiple component path string into a
|
|
single component name.
|
|
|
|
- After copying each path component, remove trailing blanks
|
|
and periods. If the last path component contained meta
|
|
characters then put a trailing period at the end of the last
|
|
component if it does contained one or more trailing periods
|
|
already.
|
|
|
|
- when unicode strings are put into the system, the code page to
|
|
unicode conversion will happen in this step.
|
|
|
|
- return the type of path via the FileType parameter, which
|
|
must be specified if the ExpectedType parameter is
|
|
CANONICALIZE_FILE_DEV_OR_PIPE or CANONICALIZE_FILE_OR_DEV.
|
|
|
|
- if this function returns an error code instead of NO_ERROR then
|
|
free the output buffer that was allocated. Otherwise set the
|
|
Length and Maximum length fields in the counted string pointed
|
|
to by the OutputString parameter.
|
|
|
|
--*/
|
|
|
|
{
|
|
PSZ Dest,
|
|
Src,
|
|
LastComponent,
|
|
BackUpLimit;
|
|
CHAR c;
|
|
ULONG Drive;
|
|
PSTRING CurrentDirectoryString;
|
|
HANDLE CurrentDirectoryHandle;
|
|
ULONG CurrentDirectoryLength;
|
|
PSZ NameBuffer;
|
|
ULONG NameBufferLength;
|
|
APIRET RetCode;
|
|
PSZ PipePrefix;
|
|
PSZ MailslotPrefix;
|
|
PSZ UNCPrefix;
|
|
PSZ ExpectedPrefix;
|
|
PSZ ConstantPrefix;
|
|
PSZ Os2Name;
|
|
int RemoveBlanksAndDots;
|
|
ULONG ExpectedPrefixLength;
|
|
BOOLEAN MetaCharactersAllowed;
|
|
BOOLEAN PreserveCase;
|
|
BOOLEAN MapToRootName;
|
|
BOOLEAN ValidateChars;
|
|
ULONG OutputFlags;
|
|
ULONG OutputType;
|
|
NTSTATUS Status;
|
|
HANDLE DeviceHandle;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
STRING DeviceName;
|
|
UNICODE_STRING DeviceName_U;
|
|
WCHAR DeviceNameBuffer[ CCHMAXPATH ];
|
|
#ifdef DBCS
|
|
// Oct.27.1993 V-AkihiS
|
|
CHAR *InvalidCharacters = Od2InvalidCharacters;
|
|
#endif
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint( "Od2Canonicalize: entering with path %s\n", Name);
|
|
}
|
|
#endif
|
|
|
|
PipePrefix = NULL;
|
|
MailslotPrefix = NULL;
|
|
UNCPrefix = NULL;
|
|
PreserveCase = FALSE;
|
|
MetaCharactersAllowed = FALSE;
|
|
MapToRootName = TRUE;
|
|
ValidateChars = TRUE;
|
|
OutputType = 0xFFFFFFFF;
|
|
OutputFlags = 0;
|
|
CurrentDirectoryString = NULL;
|
|
CurrentDirectoryHandle = NULL;
|
|
Os2Name = NULL;
|
|
RetCode = NO_ERROR;
|
|
|
|
//
|
|
// determine the expected and constant prefixes of the path,
|
|
// where the constant prefix is the
|
|
//
|
|
|
|
switch( ExpectedType ) {
|
|
case CANONICALIZE_FILE_DEV_OR_PIPE:
|
|
case CANONICALIZE_FILE_OR_DEV:
|
|
case CANONICALIZE_MAILSLOT:
|
|
PipePrefix = "\\PIPE\\";
|
|
UNCPrefix = "\\UNC\\";
|
|
ExpectedPrefix = NULL;
|
|
ConstantPrefix = "\\OS2SS\\DRIVES";
|
|
PreserveCase = TRUE;
|
|
MetaCharactersAllowed = TRUE;
|
|
MapToRootName = FALSE;
|
|
OutputType = FILE_TYPE_FILE;
|
|
MailslotPrefix = "\\MAILSLOT\\";
|
|
break;
|
|
|
|
case CANONICALIZE_SHARED_MEMORY:
|
|
ExpectedPrefix = DA_SHAREMEM_NAMEPREFIX;
|
|
ConstantPrefix = "\\OS2SS\\SHAREMEM";
|
|
break;
|
|
|
|
case CANONICALIZE_SEMAPHORE:
|
|
ExpectedPrefix = DC_SEM_NAMEPREFIX;
|
|
ConstantPrefix = "\\OS2SS\\SEMAPHORES";
|
|
break;
|
|
|
|
case CANONICALIZE_QUEUE:
|
|
ExpectedPrefix = DC_QUEUES_NAMEPREFIX;
|
|
ConstantPrefix = "\\OS2SS\\QUEUES";
|
|
break;
|
|
|
|
default:
|
|
return( ERROR_INVALID_FUNCTION );
|
|
}
|
|
|
|
if (ExpectedPrefix != NULL) {
|
|
ExpectedPrefixLength = strlen( ExpectedPrefix );
|
|
}
|
|
|
|
try {
|
|
if (*Name == '\0')
|
|
return ERROR_FILE_NOT_FOUND;
|
|
|
|
if (*Name == ' ' && *(Name+1) == '\0')
|
|
return ERROR_PATH_NOT_FOUND;
|
|
|
|
//
|
|
// initialize DirectoryHandle to indicate full path returned.
|
|
//
|
|
|
|
if (DirectoryHandle != NULL) {
|
|
*DirectoryHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// allocate name buffer. make it big enough that we can't overrun the
|
|
// end of it.
|
|
//
|
|
|
|
NameBufferLength = CCHMAXPATH + strlen(Name)+ CANONICALIZE_MAX_PREFIX_LENGTH;
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
Od2ExitGP();
|
|
}
|
|
|
|
if (( NameBuffer = RtlAllocateHeap(Od2Heap,0,NameBufferLength)) == NULL )
|
|
{
|
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
Dest = NameBuffer;
|
|
Src = Name;
|
|
|
|
|
|
|
|
//
|
|
// if we're doing a canonicalization for an IO system API (a file, device,
|
|
// UNC, or pipe name is expected), we
|
|
// detect UNC - insert correct prefix
|
|
// detect devices - insert correct prefix
|
|
// insert drive letter if needed - insert correct prefix
|
|
// insert current directory if needed
|
|
//
|
|
|
|
if ((ExpectedType == CANONICALIZE_FILE_DEV_OR_PIPE) ||
|
|
(ExpectedType == CANONICALIZE_FILE_OR_DEV) ||
|
|
(ExpectedType == CANONICALIZE_MAILSLOT)
|
|
) {
|
|
|
|
//
|
|
// detect UNC and pipe names
|
|
//
|
|
// NOTES on UNC naming conventions:
|
|
//
|
|
// Two or three leading slashes are OK, and unmodified.
|
|
//
|
|
// Four or more leading slashes are compressed to three
|
|
// leading slashes.
|
|
//
|
|
// Any number of slashes as a separator, in a local or UNC
|
|
// path, are compressed to a single slash.
|
|
//
|
|
// The \\ is a short hand for the 'current name space' whereas
|
|
// three slashes are the root of the world-wide name space.
|
|
//
|
|
//
|
|
|
|
CurrentDirectoryString = NULL;
|
|
if (ISSLASH(Src[0])) {
|
|
if (ISSLASH(Src[1])) {
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint( "processing UNC name\n");
|
|
}
|
|
#endif
|
|
OutputType = FILE_TYPE_UNC;
|
|
|
|
//
|
|
// copy constant prefix to buffer
|
|
//
|
|
ConstantPrefix = "\\OS2SS";
|
|
while (*Dest++ = *ConstantPrefix++)
|
|
;
|
|
Dest--;
|
|
|
|
Os2Name = Dest; // Os2Name points to beginning of OS/2 name
|
|
|
|
while (*Dest++ = *UNCPrefix++)
|
|
;
|
|
Dest--;
|
|
|
|
Src += 2; // for the two slashes
|
|
if (ISSLASH(*Src)) {
|
|
RetCode = ERROR_BAD_NETPATH;
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
|
|
Os2Name = Dest-1; // Os2Name points to beginning of OS/2 name
|
|
|
|
/*
|
|
path is \OS2SS\UNC\
|
|
copy the server name.
|
|
*/
|
|
|
|
while (!ISPATHSEP(*Src)) {
|
|
if ((*Src == '?') ||
|
|
((*Src == '*') && (ExpectedType != CANONICALIZE_MAILSLOT))) {
|
|
RetCode = ERROR_BAD_NETPATH;
|
|
goto ErrorExit;
|
|
}
|
|
#ifdef JAPAN
|
|
// MSKK Feb.18.1993 V-AkihiS
|
|
if (Ow2NlsIsDBCSLeadByte(*Src, SesGrp->DosCP)) {
|
|
*Dest++ = *Src++;
|
|
if (*Src) {
|
|
//
|
|
// Check Trailing byte is valid or not.
|
|
//
|
|
if ((UCHAR)*Src < 0x40 || (UCHAR)*Src > 0xFC || (UCHAR)*Src == 0x7F) {
|
|
RetCode = ERROR_BAD_NETPATH;
|
|
goto ErrorExit;
|
|
} else {
|
|
*Dest++ = *Src++;
|
|
}
|
|
} else {
|
|
RetCode = ERROR_BAD_NETPATH;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else {
|
|
if (ValidateChars && ((UCHAR)*Src < (UCHAR)' ' ||
|
|
strchr( InvalidCharacters, *Src )
|
|
)
|
|
) {
|
|
RetCode = ERROR_BAD_NETPATH;
|
|
goto ErrorExit;
|
|
}
|
|
*Dest++ = *Src++;
|
|
}
|
|
#else
|
|
if (ValidateChars && ((UCHAR)*Src < (UCHAR)' ' ||
|
|
strchr( Od2InvalidCharacters, *Src )
|
|
)
|
|
) {
|
|
RetCode = ERROR_BAD_NETPATH;
|
|
goto ErrorExit;
|
|
}
|
|
*Dest++ = *Src++;
|
|
#endif
|
|
}
|
|
if (*Src == '\0') {
|
|
RetCode = ERROR_BAD_NETPATH;
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
*Dest++ = (CHAR)OBJ_NAME_PATH_SEPARATOR; // append '\'
|
|
Src++;
|
|
while (ISSLASH(*Src))
|
|
Src++;
|
|
BackUpLimit = Dest; // set up backup pointer. points after server name
|
|
|
|
/*
|
|
at this point, we have a UNC name composed of
|
|
\OS2SS\UNC\servername\
|
|
^
|
|
|
|
|
dest
|
|
|
|
\\\\servername\\\sharepoint
|
|
^
|
|
|
|
|
src
|
|
*/
|
|
}
|
|
else if (TestForPipe(Src)) { // \PIPE\ ?
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint( "processing pipe name\n");
|
|
}
|
|
#endif
|
|
OutputType = FILE_TYPE_NMPIPE;
|
|
|
|
//
|
|
// this code assumes that pipes are in \OS2SS
|
|
// copy \OS2SS\PIPE into buffer
|
|
//
|
|
|
|
//
|
|
// we can't use drives for pipes
|
|
// (see ..\server\srvname.c)
|
|
//
|
|
ConstantPrefix = "\\OS2SS";
|
|
while (*Dest++ = *ConstantPrefix++)
|
|
;
|
|
Dest--;
|
|
|
|
Os2Name = Dest; // Os2Name points to beginning of OS/2 name
|
|
|
|
while (*Dest++ = *PipePrefix++)
|
|
;
|
|
Dest--;
|
|
|
|
/*
|
|
update src pointer past \pipe\
|
|
*/
|
|
|
|
while (ISSLASH(*Src)) // eat any extra slashes
|
|
Src++;
|
|
Src += 5;
|
|
while (ISSLASH(*Src)) // eat any extra slashes
|
|
Src++;
|
|
if (*Src == '\0') {
|
|
RetCode = ERROR_FILE_NOT_FOUND;
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
ValidateChars = TRUE;
|
|
BackUpLimit = Dest; // set up backup pointer. points after "\pipe\"
|
|
}
|
|
else if (TestForMailslot(Src)) { // \MAILSLOT\ ?
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint( "processing mailslot name\n");
|
|
}
|
|
#endif
|
|
OutputType = FILE_TYPE_MAILSLOT;
|
|
|
|
//
|
|
// this code assumes that mailslots are in \OS2SS
|
|
// copy \OS2SS\MAILSLOT into buffer
|
|
//
|
|
|
|
//
|
|
// we can't use drives for maislots
|
|
// (see ..\server\srvname.c)
|
|
//
|
|
ConstantPrefix = "\\OS2SS";
|
|
while (*Dest++ = *ConstantPrefix++)
|
|
;
|
|
Dest--;
|
|
|
|
Os2Name = Dest; // Os2Name points to beginning of OS/2 name
|
|
|
|
while (*Dest++ = *MailslotPrefix++)
|
|
;
|
|
Dest--;
|
|
|
|
/*
|
|
update src pointer past \mailslot\
|
|
*/
|
|
|
|
while (ISSLASH(*Src)) // eat any extra slashes
|
|
Src++;
|
|
Src += 9;
|
|
while (ISSLASH(*Src)) // eat any extra slashes
|
|
Src++;
|
|
if (*Src == '\0') {
|
|
RetCode = ERROR_FILE_NOT_FOUND;
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
ValidateChars = TRUE;
|
|
BackUpLimit = Dest; // set up backup pointer. points after "\pipe\"
|
|
}
|
|
}
|
|
|
|
// haven't detected pipe or UNC
|
|
if (OutputType == FILE_TYPE_FILE) {
|
|
|
|
#ifdef DBCS
|
|
// MSKK Oct.27.1993 V-AkihIS
|
|
InvalidCharacters = Od2FatInvalidCharacters;
|
|
#endif
|
|
/*
|
|
detect devices here.
|
|
device names are recognized as follows:
|
|
installed/default devices are allowed in any d:\path
|
|
pseudochar devices must match exactly, starting with \DEV\
|
|
*/
|
|
|
|
LastComponent = FindLastComponent(Src);
|
|
|
|
if (*LastComponent)
|
|
{
|
|
PWSTR DotPlace;
|
|
USHORT NewLen;
|
|
|
|
//
|
|
// Open as a symbolic link, relative to the OS/2 Device Directory in the
|
|
// object name space, the last component of the file path.
|
|
//
|
|
|
|
Od2InitMBString( &DeviceName, LastComponent );
|
|
|
|
//
|
|
// UNICODE conversion -
|
|
//
|
|
|
|
RetCode = Od2MBStringToUnicodeString(
|
|
&DeviceName_U,
|
|
&DeviceName,
|
|
TRUE);
|
|
|
|
if (RetCode)
|
|
{
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS )
|
|
{
|
|
DbgPrint("Od2Canonicalize: no memory for Unicode Conversion\n");
|
|
}
|
|
#endif
|
|
//return RetCode;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
for (DotPlace = DeviceName_U.Buffer, NewLen = 0;
|
|
NewLen < DeviceName_U.Length;
|
|
DotPlace++, NewLen += sizeof(WCHAR)) {
|
|
|
|
if ((*DotPlace == L'.') || (*DotPlace == L' ')) {
|
|
|
|
DeviceName_U.Length = NewLen;
|
|
break;
|
|
}
|
|
}
|
|
|
|
InitializeObjectAttributes( &ObjectAttributes,
|
|
&DeviceName_U,
|
|
OBJ_CASE_INSENSITIVE,
|
|
Od2DeviceDirectory,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenSymbolicLinkObject( &DeviceHandle,
|
|
SYMBOLIC_LINK_QUERY,
|
|
&ObjectAttributes
|
|
);
|
|
RtlFreeUnicodeString (&DeviceName_U);
|
|
if (NT_SUCCESS( Status )) {
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint( "processing device name\n");
|
|
}
|
|
#endif
|
|
//
|
|
// Found a symbolic link in the OS/2 Device Directory whose
|
|
// name matches the last component of the file path. Query
|
|
// the target string from the symbolic link.
|
|
//
|
|
|
|
DeviceName_U.Length = 0;
|
|
DeviceName_U.MaximumLength = sizeof( DeviceNameBuffer );
|
|
DeviceName_U.Buffer = DeviceNameBuffer;
|
|
Status = NtQuerySymbolicLinkObject( DeviceHandle,
|
|
&DeviceName_U,
|
|
NULL
|
|
);
|
|
NtClose(DeviceHandle);
|
|
if (NT_SUCCESS( Status )) {
|
|
//
|
|
// Successfully queried the target string, so copy it as
|
|
// is to the output path buffer, obliterating what was there.
|
|
// Next determine the file type by examining the first
|
|
// character of the target string. @n is a pseudo-device
|
|
// builtin to the OS/2 subsystem. Otherwise it is a physical
|
|
// NT device path.
|
|
//
|
|
|
|
RetCode = Od2UnicodeStringToMBString( &DeviceName, &DeviceName_U, TRUE );
|
|
if (RetCode)
|
|
{
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS )
|
|
{
|
|
DbgPrint("Od2Canonicalize: no memory for Unicode Conversion-2\n");
|
|
}
|
|
#endif
|
|
//return RetCode;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
strncpy( Dest,
|
|
&DeviceName.Buffer[1],
|
|
DeviceName.Length - 1
|
|
);
|
|
|
|
*(Dest + DeviceName.Length - 1) = '\0'; // for debug printing
|
|
|
|
if (DeviceName.Buffer[0] == '@') {
|
|
OutputType = FILE_TYPE_PSDEV;
|
|
}
|
|
else if (DeviceName.Buffer[0] == '#') {
|
|
OutputType = FILE_TYPE_COM;
|
|
}
|
|
else {
|
|
OutputType = FILE_TYPE_DEV;
|
|
}
|
|
CanonicalString->MaximumLength = (USHORT) NameBufferLength;
|
|
CanonicalString->Length = DeviceName.Length - 1;
|
|
CanonicalString->Buffer = NameBuffer;
|
|
Od2FreeMBString( &DeviceName );
|
|
goto ErrorExit;
|
|
}
|
|
else {
|
|
RetCode = ERROR_PATH_NOT_FOUND; // FIX, FIX - Should never happen.
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// if we didn't detect a device, we have a file. add a drive letter
|
|
// and current directory, if necessary.
|
|
//
|
|
|
|
if (OutputType == FILE_TYPE_FILE) {
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint( "processing file name\n");
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// A xxxx\ is not allowed
|
|
//
|
|
#ifdef DBCS
|
|
// MSKK Feb.26.1993 V-AkihiS
|
|
{
|
|
ULONG i = 0;
|
|
BOOLEAN LastSlashFlag = FALSE;
|
|
BOOLEAN ColonFlag = FALSE;
|
|
|
|
//
|
|
// Check last charater is '\' and privious character of last
|
|
// character is not ':'. If so(i.e. LastSlashFlag is true,
|
|
// when exiting while-loop), it is not allowed.
|
|
//
|
|
while (i < strlen(Name)) {
|
|
if (Ow2NlsIsDBCSLeadByte(Name[i], SesGrp->DosCP)) {
|
|
LastSlashFlag = ColonFlag = FALSE;
|
|
i++;
|
|
if (i < strlen(Name)) i ++;
|
|
} else {
|
|
if (Name[i] == ':') {
|
|
LastSlashFlag = FALSE;
|
|
ColonFlag = TRUE;
|
|
} else if (ISSLASH(Name[i])) {
|
|
if (ColonFlag) {
|
|
LastSlashFlag = FALSE;
|
|
} else {
|
|
LastSlashFlag = TRUE;
|
|
}
|
|
ColonFlag = FALSE;
|
|
} else {
|
|
LastSlashFlag = ColonFlag = FALSE;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
if ( LastSlashFlag && (strlen(Name) > 1)) {
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint( "Canonicalize: filename is D:xxx\\, return ERROR_PATH_NOT_FOUND \n");
|
|
}
|
|
#endif
|
|
return(ERROR_PATH_NOT_FOUND);
|
|
}
|
|
|
|
}
|
|
#else
|
|
if ( ISSLASH(Name[strlen(Name)-1]) && (strlen(Name) > 1 )
|
|
&& (Name[strlen(Name)-2] != ':') /* slash root is a fine name */ ) {
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint( "Canonicalize: filename is D:xxx\\, return ERROR_PATH_NOT_FOUND \n");
|
|
}
|
|
#endif
|
|
return(ERROR_PATH_NOT_FOUND);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// copy the constant prefix.
|
|
//
|
|
|
|
while (*Dest++ = *ConstantPrefix++)
|
|
;
|
|
Dest--;
|
|
*Dest++ = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
Os2Name = Dest; // Os2Name points to beginning of OS/2 name
|
|
#ifdef DBCS
|
|
// MSKK Apr.11.1993 V-AkihiS
|
|
if (!(*(Src+1) == ':' && !IsDBCSLeadByte(*Src))) { // if no drive letter, get one
|
|
#else
|
|
if (*(Src+1) != ':') { // if no drive letter, get one
|
|
#endif
|
|
*Dest++ = CONVERTTOASCII(Od2CurrentDisk);
|
|
*Dest++ = ':';
|
|
Drive = Od2CurrentDisk;
|
|
}
|
|
else {
|
|
|
|
// figure out which zero-based drive to use
|
|
|
|
if (*Src > 'Z') {
|
|
Drive = *Src - 'a';
|
|
}
|
|
else {
|
|
Drive = *Src - 'A';
|
|
}
|
|
if (RetCode = VerifyDriveExists(Drive+1)){
|
|
return(RetCode);
|
|
}
|
|
*Dest++ = *Src++; // copy drive letter
|
|
*Dest++ = *Src++;
|
|
if (*Src == '\0') {
|
|
RetCode = ERROR_FILE_NOT_FOUND;
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
}
|
|
|
|
//
|
|
// Buffer contains \\OS2SS\DRIVES\D:
|
|
// ^
|
|
// |
|
|
// dest
|
|
//
|
|
// d:\foo
|
|
// ^
|
|
// |
|
|
// src
|
|
//
|
|
// set up backup pointer
|
|
|
|
BackUpLimit = Dest; // set up backup pointer. points to first '\'
|
|
|
|
//
|
|
// prepend logondirectory here
|
|
//
|
|
// if (relative path)
|
|
// copy current directory;
|
|
// append '\';
|
|
// else
|
|
// copy first '\';
|
|
//
|
|
|
|
if (!ISSLASH(*Src)) { // if path not absolute
|
|
|
|
//
|
|
// if no current directory, append '\'
|
|
//
|
|
|
|
*Dest++ = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
RetCode = Od2GetCurrentDirectory(Drive,
|
|
&CurrentDirectoryString,
|
|
&CurrentDirectoryHandle,
|
|
&CurrentDirectoryLength,
|
|
FALSE
|
|
);
|
|
if (RetCode != NO_ERROR) {
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("GetCurrentDirectory returned %s\n",CurrentDirectoryString->Buffer);
|
|
}
|
|
#endif
|
|
if (CurrentDirectoryHandle != NULL) { // if not root directory
|
|
CurrentDirectoryString->Length -= DRIVE_LETTER_SIZE + FILE_PREFIX_LENGTH;
|
|
CurrentDirectoryString->Buffer += DRIVE_LETTER_SIZE + FILE_PREFIX_LENGTH;
|
|
strncpy(Dest,
|
|
CurrentDirectoryString->Buffer,
|
|
CurrentDirectoryString->Length
|
|
);
|
|
Dest += CurrentDirectoryString->Length;
|
|
*Dest++ = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
}
|
|
}
|
|
else { // copy one '\' and eat any others
|
|
*Dest++ = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
Src++;
|
|
while (ISSLASH(*Src))
|
|
Src++;
|
|
}
|
|
|
|
/*
|
|
the buffer contains:
|
|
\OS2SS\DRIVES\D:\currentdir\
|
|
^
|
|
|
|
|
dest
|
|
d:foo
|
|
^
|
|
|
|
|
src
|
|
*/
|
|
}
|
|
}
|
|
|
|
//
|
|
// we're doing canonicalization for a non IO system API - semaphores,
|
|
// queues, or shared memory. we check for the expected prefix and copy
|
|
// it to the buffer. there can be any number of slashes anywhere in
|
|
// the name. i.e. \\/\SEM32/////foo -> \SEM32\foo.
|
|
//
|
|
|
|
else {
|
|
|
|
//
|
|
// copy constant prefix to buffer
|
|
//
|
|
|
|
while (*Dest++ = *ConstantPrefix++)
|
|
;
|
|
Dest--;
|
|
|
|
//
|
|
// verify that the expected prefix is there. advance Src past
|
|
// all leading slashes.
|
|
//
|
|
|
|
if (ISSLASH(*Src)) {
|
|
do {
|
|
Src++;
|
|
} while (ISSLASH(*Src));
|
|
|
|
//
|
|
// use the strnicmp to compare for the text part of the prefix and
|
|
// ISSLASH to compare for the trailing slash.
|
|
//
|
|
|
|
#ifdef DBCS
|
|
// MSKK Feb.26.1993 V-AkihiS
|
|
{
|
|
ULONG i = 0;
|
|
BOOLEAN SlashFlag = FALSE;
|
|
|
|
//
|
|
// verify whether the last character of the text part of
|
|
// the prefix is slash or not.
|
|
//
|
|
while (i < ExpectedPrefixLength - 1) {
|
|
if (Ow2NlsIsDBCSLeadByte(*(Src+i), SesGrp->DosCP)) {
|
|
SlashFlag = FALSE;
|
|
i++;
|
|
if (i < ExpectedPrefixLength - 1) {
|
|
i++;
|
|
}
|
|
} else {
|
|
if (ISSLASH(*(Src+i))) {
|
|
SlashFlag = TRUE;
|
|
} else {
|
|
SlashFlag = FALSE;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
if (_strnicmp( Src, ExpectedPrefix+1, ExpectedPrefixLength-2 ) ||
|
|
!SlashFlag) {
|
|
RetCode = ERROR_PATH_NOT_FOUND;
|
|
}
|
|
}
|
|
#else
|
|
if (_strnicmp( Src, ExpectedPrefix+1 , ExpectedPrefixLength-2 ) ||
|
|
!(ISSLASH(*(Src+ExpectedPrefixLength-2)))) {
|
|
RetCode = ERROR_PATH_NOT_FOUND;
|
|
}
|
|
#endif
|
|
|
|
Os2Name = Dest; // Os2Name points to beginning of OS/2 name
|
|
|
|
//
|
|
// copy the expected prefix. Make sure terminating slash is
|
|
// the correct type.
|
|
//
|
|
|
|
while (c = *ExpectedPrefix++) {
|
|
if (c == (CHAR)OBJ_NAME_PATH_SEPARATOR && MapToRootName) {
|
|
c = '/';
|
|
}
|
|
*Dest++ = c;
|
|
}
|
|
BackUpLimit = Dest-1; // backup pointer. points to last '\'
|
|
|
|
//
|
|
// update the src pointer past the prefix and slashes
|
|
//
|
|
|
|
Src += ExpectedPrefixLength-1;
|
|
while (ISSLASH(*Src)) {
|
|
Src++;
|
|
}
|
|
|
|
//
|
|
// error if nothing after expected prefix
|
|
//
|
|
|
|
if (*Src == '\0') {
|
|
RetCode = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
}
|
|
else {
|
|
RetCode = ERROR_PATH_NOT_FOUND;
|
|
}
|
|
|
|
if (RetCode) {
|
|
goto ErrorExit;
|
|
}
|
|
|
|
/* target
|
|
|
|
|
V
|
|
at this point, the target contains \OS2SS\xxx\xxx\
|
|
source
|
|
|
|
|
the source points after the expected prefix V
|
|
\SEM32\\
|
|
*/
|
|
|
|
}
|
|
|
|
/*
|
|
copy user string
|
|
|
|
*Dest -> first char after d:\
|
|
*src -> first char after d:\
|
|
|
|
if we see a metacharacter, we verify that they're allowed. if there
|
|
is a metacharacter in any component other than the last, we return
|
|
an error.
|
|
*/
|
|
|
|
LastComponent = Dest; // LastComponent points to first letter
|
|
while (*Src) {
|
|
|
|
//
|
|
// if we get here and meta characters have already been found, they
|
|
// aren't in the last component, so we return an error.
|
|
//
|
|
|
|
if (OutputFlags & CANONICALIZE_META_CHARS_FOUND) {
|
|
RetCode = ERROR_PATH_NOT_FOUND;
|
|
goto ErrorExit;
|
|
}
|
|
RemoveBlanksAndDots=TRUE;
|
|
|
|
//
|
|
// if path is '.', copy . and don't remove dots
|
|
//
|
|
|
|
if ((*Src == '.') &&
|
|
(ISPATHSEP(Src[1]))) {
|
|
Src++;
|
|
Dest--;
|
|
RemoveBlanksAndDots=FALSE;
|
|
}
|
|
|
|
//
|
|
// else if path is '..', copy .. and don't remove dots
|
|
//
|
|
|
|
else if ((Src[0] == '.') &&
|
|
(Src[1] == '.') ) {
|
|
if (Src[2] == '.') {
|
|
RetCode = ERROR_PATH_NOT_FOUND;
|
|
goto ErrorExit;
|
|
}
|
|
if (ISPATHSEP(Src[2])) {
|
|
Src += 2; // Src points past ..
|
|
Dest -= 2; // Dest points to char before last path sep
|
|
#ifdef DBCS
|
|
// MSKK Feb.28.1993 V-AkihiS
|
|
{
|
|
PSZ String, LastSlashPtr;
|
|
|
|
String = LastSlashPtr = NameBuffer;
|
|
while (String <= Dest) {
|
|
if (Ow2NlsIsDBCSLeadByte(*String, SesGrp->DosCP)) {
|
|
String++;
|
|
if (String <= Dest) {
|
|
String++;
|
|
}
|
|
} else {
|
|
if (ISSLASH(*String)) {
|
|
if (String >= BackUpLimit) {
|
|
LastSlashPtr = String;
|
|
}
|
|
}
|
|
String++;
|
|
}
|
|
}
|
|
|
|
if (ISSLASH(*LastSlashPtr)) {
|
|
Dest = LastSlashPtr;
|
|
} else {
|
|
Dest = BackUpLimit - 1;
|
|
RetCode = ERROR_PATH_NOT_FOUND;
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
}
|
|
#else
|
|
while ((Dest >= BackUpLimit) && !(ISSLASH(*Dest))) { // have to use ISSLASH because of MapToRootName
|
|
Dest--; // BUGBUG make DBCS correct
|
|
}
|
|
if (!ISSLASH(*Dest)) {
|
|
RetCode = ERROR_PATH_NOT_FOUND;
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
#endif
|
|
RemoveBlanksAndDots=FALSE;
|
|
}
|
|
else {
|
|
RetCode = ERROR_FILE_NOT_FOUND;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// else copy path
|
|
//
|
|
|
|
else {
|
|
while (!ISPATHSEP(*Src)) {
|
|
c = *Src++;
|
|
if ((c == '?') || (c == '*')) {
|
|
if (!MetaCharactersAllowed) {
|
|
RetCode = ERROR_PATH_NOT_FOUND;
|
|
goto ErrorExit;
|
|
}
|
|
else {
|
|
OutputFlags |= CANONICALIZE_META_CHARS_FOUND;
|
|
}
|
|
}
|
|
#ifdef DBCS
|
|
// MSKK Apr.18.1993 V-AkihiS
|
|
if (Ow2NlsIsDBCSLeadByte(c, SesGrp->DosCP)) {
|
|
*Dest++ = c;
|
|
if (*Src) {
|
|
//
|
|
// Check Trailing byte is valid or not.
|
|
//
|
|
if ((UCHAR)*Src < 0x40 || (UCHAR)*Src > 0xFC || (UCHAR)*Src == 0x7F) {
|
|
RetCode = ERROR_INVALID_NAME;
|
|
goto ErrorExit;
|
|
} else {
|
|
*Dest++ = *Src++;
|
|
}
|
|
} else {
|
|
RetCode = ERROR_INVALID_NAME;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else {
|
|
if (ValidateChars && ((UCHAR)c < (UCHAR)' ' ||
|
|
strchr( InvalidCharacters, c )
|
|
)
|
|
) {
|
|
RetCode = ERROR_INVALID_NAME;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((!PreserveCase) && (c >= 'a' && c <= 'z')) {
|
|
c = (CHAR) (c - 'a' + 'A');
|
|
}
|
|
*Dest++ = c;
|
|
}
|
|
#else
|
|
if (ValidateChars && ((UCHAR)c < (UCHAR)' ' ||
|
|
strchr( Od2InvalidCharacters, c )
|
|
)
|
|
) {
|
|
RetCode = ERROR_INVALID_NAME;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((!PreserveCase) && (c >= 'a' && c <= 'z')) {
|
|
c = (CHAR) (c - 'a' + 'A');
|
|
}
|
|
*Dest++ = c;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//
|
|
// truncate trailing dots and blanks here. if there is a metacharacter
|
|
// in the name, we must leave one trailing dot, if there is one.
|
|
// DBCS correct because '.' and ' ' aren't valid trailing bytes
|
|
//
|
|
|
|
if (RemoveBlanksAndDots) {
|
|
if ((Dest > LastComponent) && ((*(Dest-1) == '.') || (*(Dest-1) == ' '))) {
|
|
do {
|
|
Dest--;
|
|
} while ((Dest > LastComponent) && ((*(Dest-1) == '.') || (*(Dest-1) == ' ')));
|
|
if (OutputFlags & CANONICALIZE_META_CHARS_FOUND) {
|
|
if (*Dest == '.') {
|
|
Dest++;
|
|
}
|
|
}
|
|
}
|
|
if (Dest <= LastComponent) {
|
|
RetCode = ERROR_FILE_NOT_FOUND;
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
}
|
|
|
|
// *Src == '\' or '/'
|
|
if (ISSLASH(*Src)) {
|
|
if (MapToRootName) {
|
|
*Dest++ = '/';
|
|
}
|
|
else {
|
|
*Dest++ = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
}
|
|
LastComponent = Dest;
|
|
|
|
while (ISSLASH(*Src)) {
|
|
Src++; // eat up extra '\'s
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// null terminate
|
|
//
|
|
|
|
*Dest = '\0';
|
|
if (OutputType != FILE_TYPE_FILE &&
|
|
OutputType != FILE_TYPE_DEV &&
|
|
OutputType != FILE_TYPE_PSDEV
|
|
) {
|
|
if (Dest == LastComponent || Dest <= BackUpLimit) {
|
|
RetCode = ERROR_FILE_NOT_FOUND;
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
|
|
if (OutputType == FILE_TYPE_NMPIPE && !TestForPipe(Os2Name)) {
|
|
RetCode = ERROR_FILE_NOT_FOUND;
|
|
goto ErrorExit;
|
|
}
|
|
else if (OutputType == FILE_TYPE_MAILSLOT && !TestForMailslot(Os2Name)) {
|
|
RetCode = ERROR_FILE_NOT_FOUND;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enforce 260 max path length. remember that length does not include
|
|
// the terminating null. Remember the 260 limit includes the null byte.
|
|
//
|
|
|
|
ASSERT (Os2Name != NULL);
|
|
if (strlen(Os2Name) > CCHMAXPATH - 1) {
|
|
RetCode = ERROR_FILENAME_EXCED_RANGE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// do some file-specific stuff.
|
|
//
|
|
|
|
if (OutputType == FILE_TYPE_FILE) {
|
|
|
|
//
|
|
// detect root directory
|
|
// test for "d:". if so, map to "d:\"
|
|
//
|
|
|
|
if (NameBuffer[FIRST_SLASH+FILE_PREFIX_LENGTH] == '\0') { // resulting path is d:
|
|
NameBuffer[FIRST_SLASH+FILE_PREFIX_LENGTH] = (CHAR)OBJ_NAME_PATH_SEPARATOR;
|
|
NameBuffer[ROOTDIRLENGTH+FILE_PREFIX_LENGTH] = '\0';
|
|
Dest++;
|
|
OutputFlags |= CANONICALIZE_IS_ROOT_DIRECTORY;
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("path is root dir\n");
|
|
}
|
|
#endif
|
|
}
|
|
else if (NameBuffer[FIRST_SLASH+FILE_PREFIX_LENGTH] == (CHAR)OBJ_NAME_PATH_SEPARATOR && NameBuffer[ROOTDIRLENGTH+FILE_PREFIX_LENGTH] == '\0') {
|
|
OutputFlags |= CANONICALIZE_IS_ROOT_DIRECTORY;
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("path is root dir\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// test for "d:\pipe" or "d:\pipe\..." this is illegal unless
|
|
// CANONICALIZE_FILE_OR_DEV is specified. only DosMove sets this flag.
|
|
//
|
|
|
|
else if ((!(_strnicmp(NameBuffer+DRIVE_LETTER_SIZE+FILE_PREFIX_LENGTH,"PIPE",4))) &&
|
|
(((*(NameBuffer+DRIVE_LETTER_SIZE+FILE_PREFIX_LENGTH+PIPE_DIR_SIZE-2)) == 0) ||
|
|
((*(NameBuffer+DRIVE_LETTER_SIZE+FILE_PREFIX_LENGTH+PIPE_DIR_SIZE-2)) == (CHAR)OBJ_NAME_PATH_SEPARATOR))) {
|
|
if (ExpectedType != CANONICALIZE_FILE_OR_DEV) {
|
|
RetCode = ERROR_PATH_NOT_FOUND;
|
|
goto ErrorExit; // go free buffer(s) and return error
|
|
}
|
|
}
|
|
|
|
//
|
|
// optimize to use open handle to current directory if 1) the user doesn't
|
|
// need the full path (i.e. not for current directory operations) 2) we
|
|
// have the current directory (the user didn't specify a full path), and
|
|
// 3) current directory isn't root.
|
|
//
|
|
|
|
if ((ARGUMENT_PRESENT( DirectoryHandle )) &&
|
|
(CurrentDirectoryHandle != NULL) &&
|
|
(!_strnicmp(NameBuffer+DRIVE_LETTER_SIZE+FILE_PREFIX_LENGTH,
|
|
CurrentDirectoryString->Buffer,
|
|
CurrentDirectoryString->Length)) &&
|
|
(NameBuffer[DRIVE_LETTER_SIZE+FILE_PREFIX_LENGTH+
|
|
CurrentDirectoryString->Length] == '\\')
|
|
) {
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("Optimized relative path\n");
|
|
}
|
|
#endif
|
|
*DirectoryHandle = CurrentDirectoryHandle;
|
|
|
|
//
|
|
// we have to return a pointer to the relative path. we can
|
|
// either allocate a second buffer and copy the name from
|
|
// the current directory on, or ripple copy the relative path
|
|
// over the full path. since the ripple copy is less work, we
|
|
// do it.
|
|
//
|
|
// if canonical path is "\os2ss\drives\a:\dir1\dir2" and current
|
|
// directory is "\os2ss\drives\a:\dir1", we want to copy "dir2"
|
|
// to the beginning of the buffer.
|
|
//
|
|
|
|
Src = NameBuffer+CurrentDirectoryString->Length+1+DRIVE_LETTER_SIZE+FILE_PREFIX_LENGTH;
|
|
Dest = NameBuffer;
|
|
while (*Dest++ = *Src++)
|
|
;
|
|
Dest--;
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint("got to here. name is %s\n",NameBuffer);
|
|
if (DirectoryHandle != NULL)
|
|
DbgPrint(" handle is %ld\n",*DirectoryHandle);
|
|
}
|
|
#endif
|
|
|
|
CanonicalString->MaximumLength = (USHORT) NameBufferLength;
|
|
CanonicalString->Length = (USHORT) (Dest - NameBuffer);
|
|
CanonicalString->Buffer = NameBuffer;
|
|
|
|
#if 0
|
|
#ifndef DBCS // MSKK move max path length check
|
|
//
|
|
// Enforce 260 max path length. remember that length does not include
|
|
// the terminating null. Remember the 260 limit includes the null byte.
|
|
//
|
|
|
|
ASSERT (Os2Name != NULL);
|
|
if (CanonicalString->Length - (Os2Name - NameBuffer) >=
|
|
CCHMAXPATH) {
|
|
RetCode = ERROR_PATH_NOT_FOUND;
|
|
}
|
|
#endif
|
|
#endif // 0
|
|
|
|
ErrorExit:
|
|
if (CurrentDirectoryString != NULL) {
|
|
RtlFreeHeap( Od2Heap, 0, CurrentDirectoryString );
|
|
}
|
|
|
|
if (RetCode != NO_ERROR) {
|
|
if (NameBuffer != NULL) {
|
|
RtlFreeHeap( Od2Heap, 0, NameBuffer );
|
|
}
|
|
}
|
|
else {
|
|
if (ARGUMENT_PRESENT( ParseFlags )) {
|
|
*ParseFlags = OutputFlags;
|
|
}
|
|
|
|
if (ExpectedPrefix == NULL && ARGUMENT_PRESENT( FileType )) {
|
|
*FileType = OutputType;
|
|
}
|
|
|
|
#if DBG
|
|
IF_OD2_DEBUG( FILESYS ) {
|
|
DbgPrint( "Od2Canonicalize: returning path %s\n", NameBuffer);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return( RetCode );
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
Od2IsAbsolutePath(
|
|
IN PSZ Path
|
|
)
|
|
{
|
|
CHAR c;
|
|
|
|
if (Path[ 0 ] && Path[ 1 ] != ':') {
|
|
while (c = *Path++) {
|
|
if (c == '/' || c == (CHAR)OBJ_NAME_PATH_SEPARATOR) {
|
|
return( TRUE );
|
|
}
|
|
#ifdef DBCS
|
|
// MSKK Apr.11.1993 V-AkihiS
|
|
if (Ow2NlsIsDBCSLeadByte(c, SesGrp->DosCP)) {
|
|
if (*Path) Path++;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
else {
|
|
return( TRUE );
|
|
}
|
|
}
|