1 ////////////////////////////////////////////////////////////////////
2 // Copyright (C) Alexander Telyatnikov, Ivan Keliukh, Yegor Anchishkin, SKIF Software, 1999-2013. Kiev, Ukraine
3 // All rights reserved
4 // This file was released under the GPLv2 on June 2015.
5 ////////////////////////////////////////////////////////////////////
6 /*
7     Module:
8             Namesup.cpp
9 
10     Abstract: FileName support routines
11 */
12 
13 #include "udffs.h"
14 
15 //  '\dfdf\aaa\ffg'  -->  '\aaa\ffg'
16 //       '\aaa\ffg'  -->  '\ffg'
17 PWCHAR
18 __fastcall
UDFDissectName(IN PWCHAR Buffer,OUT PUSHORT Length)19 UDFDissectName(
20     IN  PWCHAR   Buffer,
21     OUT PUSHORT  Length
22     )
23 {
24 
25     USHORT  i;
26 
27 #if defined(_X86_) && defined(_MSC_VER) && !defined(__clang__)
28 
29     PWCHAR retval;
30 
31     __asm push  ebx
32     __asm push  ecx
33 
34     __asm mov   ebx,Buffer
35     __asm xor   ecx,ecx
36 Remove_leading_slash:
37     __asm cmp   [word ptr ebx],L'\\'
38     __asm jne   No_IncPointer
39     __asm add   ebx,2
40     __asm jmp   Remove_leading_slash
41 No_IncPointer:
42     __asm cmp   [word ptr ebx],L':'
43     __asm jne   Scan_1
44     __asm add   ebx,2
45     __asm inc   ecx
46     __asm jmp   EO_Dissect
47 Scan_1:
48     __asm mov   ax,[word ptr ebx]
49     __asm cmp   ax,L'\\'
50     __asm je    EO_Dissect
51     __asm or    ax,ax
52     __asm jz    EO_Dissect
53     __asm cmp   ax,L':'
54     __asm jne   Cont_scan
55     __asm or    ecx,ecx
56     __asm jnz   EO_Dissect
57 Cont_scan:
58     __asm inc   ecx
59     __asm add   ebx,2
60     __asm jmp   Scan_1
61 EO_Dissect:
62     __asm mov   retval,ebx
63     __asm mov   i,cx
64 
65     __asm pop   ecx
66     __asm pop   ebx
67 
68     *Length = i;
69     return retval;
70 
71 #else   // NO X86 optimization , use generic C/C++
72 
73     while (Buffer[0] == L'\\') {
74         Buffer++;
75     }
76     if (Buffer[0] == L':') {
77         *Length = 1;
78         return &(Buffer[1]);
79     }
80     for(i = 0; (  Buffer[i] != L'\\' &&
81                 ((Buffer[i] != L':') || !i) &&
82                   Buffer[i]);  i++);
83     *Length = i;
84     return &(Buffer[i]);
85 
86 #endif // _X86_
87 
88 } // end UDFDissectName()
89 
90 BOOLEAN
91 __fastcall
UDFIsNameValid(IN PUNICODE_STRING SearchPattern,OUT BOOLEAN * StreamOpen,OUT ULONG * SNameIndex)92 UDFIsNameValid(
93     IN PUNICODE_STRING SearchPattern,
94     OUT BOOLEAN* StreamOpen,
95     OUT ULONG* SNameIndex
96 ) {
97     LONG   Index, l;
98     BOOLEAN _StreamOpen = FALSE;
99     PWCHAR Buffer;
100     WCHAR c, c0;
101 
102     if(StreamOpen) (*StreamOpen) = FALSE;
103     // We can't create nameless file or too long path
104     if(!(l = SearchPattern->Length/sizeof(WCHAR)) ||
105         (l>UDF_X_PATH_LEN)) return FALSE;
106     Buffer = SearchPattern->Buffer;
107     for(Index = 0; Index<l; Index++, Buffer++) {
108         // Check for disallowed characters
109         c = (*Buffer);
110         if((c == L'*') ||
111            (c == L'>') ||
112            (c == L'\"') ||
113            (c == L'/') ||
114            (c == L'<') ||
115            (c == L'|') ||
116            ((c >= 0x0000) && (c <= 0x001f)) ||
117            (c == L'?')) return FALSE;
118         // check if this a Stream path (& validate it)
119         if(!(_StreamOpen) && // sub-streams are not allowed
120             (Index<(l-1)) && // stream name must be specified
121            ((_StreamOpen) = (c == L':'))) {
122             if(StreamOpen) (*StreamOpen) = TRUE;
123             if(SNameIndex) (*SNameIndex) = Index;
124         }
125         // According to NT IFS documentation neither SPACE nor DOT can be
126         // a trailing character
127         if(Index && (c == L'\\') ) {
128            if((c0 == L' ') ||
129               (_StreamOpen) || // stream is not a directory
130               (c0 == L'.')) return FALSE;
131         }
132         c0 = c;
133     }
134     // According to NT IFS documentation neither SPACE nor DOT can be
135     // a trailing character
136     if((c0 == L' ') ||
137        (c0 == L'.')) return FALSE;
138     return TRUE;
139 } // end UDFIsNameValid()
140 
141 
142 #ifndef _CONSOLE
143 /*
144 
145 Routine Description:
146 
147     This routine will compare two Unicode strings.
148     PtrSearchPattern may contain wildcards
149 
150 Return Value:
151 
152     BOOLEAN - TRUE if the expressions match, FALSE otherwise.
153 
154 */
155 BOOLEAN
UDFIsNameInExpression(IN PVCB Vcb,IN PUNICODE_STRING FileName,IN PUNICODE_STRING PtrSearchPattern,OUT PBOOLEAN DosOpen,IN BOOLEAN IgnoreCase,IN BOOLEAN ContainsWC,IN BOOLEAN CanBe8dot3,IN BOOLEAN KeepIntact)156 UDFIsNameInExpression(
157     IN PVCB Vcb,
158     IN PUNICODE_STRING FileName,
159     IN PUNICODE_STRING PtrSearchPattern,
160     OUT PBOOLEAN DosOpen,
161     IN BOOLEAN IgnoreCase,
162     IN BOOLEAN ContainsWC,
163     IN BOOLEAN CanBe8dot3,
164     IN BOOLEAN KeepIntact // passed to UDFDOSName
165     )
166 {
167     BOOLEAN             Match = TRUE;
168     UNICODE_STRING      ShortName;
169     WCHAR               Buffer[13];
170 
171     if(!PtrSearchPattern) return TRUE;
172     // we try to open file by LFN by default
173     if(DosOpen) (*DosOpen) = FALSE;
174     //  If there are wildcards in the expression then we call the
175     //  appropriate FsRtlRoutine.
176     if(ContainsWC) {
177         Match = FsRtlIsNameInExpression( PtrSearchPattern, FileName, IgnoreCase, NULL );
178     //  Otherwise do a direct memory comparison for the name string.
179     } else if (RtlCompareUnicodeString(FileName, PtrSearchPattern, IgnoreCase)) {
180         Match = FALSE;
181     }
182 
183     if(Match) return TRUE;
184 
185     // check if SFN can match this pattern
186     if(!CanBe8dot3)
187         return FALSE;
188 
189     // try to open by SFN
190     ShortName.Buffer = (PWCHAR)(&Buffer);
191     ShortName.MaximumLength = 13*sizeof(WCHAR);
192     UDFDOSName(Vcb, &ShortName, FileName, KeepIntact);
193 
194     // PtrSearchPattern is upcased if we are called with IgnoreCase=TRUE
195     // DOSName is always upcased
196     // thus, we can use case-sensetive compare here to improve performance
197     if(ContainsWC) {
198         Match = FsRtlIsNameInExpression( PtrSearchPattern, &ShortName, FALSE, NULL );
199     //  Otherwise do a direct memory comparison for the name string.
200     } else if (!RtlCompareUnicodeString(&ShortName, PtrSearchPattern, FALSE)) {
201         Match = TRUE;
202     }
203     if(DosOpen && Match) {
204         // remember that we've opened file by SFN
205         (*DosOpen) = TRUE;
206     }
207     return Match;
208 } // end UDFIsNameInExpression()
209 
210 #endif
211 
212 BOOLEAN
213 __fastcall
UDFIsMatchAllMask(IN PUNICODE_STRING Name,OUT BOOLEAN * DosOpen)214 UDFIsMatchAllMask(
215     IN PUNICODE_STRING Name,
216    OUT BOOLEAN* DosOpen
217     )
218 {
219     USHORT i;
220     PWCHAR Buffer;
221 
222     if(DosOpen)
223         *DosOpen = FALSE;
224     Buffer = Name->Buffer;
225     if(Name->Length == sizeof(WCHAR)) {
226         // Win32-style wildcard
227         if((*Buffer) != L'*')
228             return FALSE;
229         return TRUE;
230     } else
231     if(Name->Length == sizeof(WCHAR)*(8+1+3)) {
232         // DOS-style wildcard
233         for(i=0;i<8;i++,Buffer++) {
234             if((*Buffer) != DOS_QM)
235                 return FALSE;
236         }
237         if((*Buffer) != DOS_DOT)
238             return FALSE;
239         Buffer++;
240         for(i=9;i<12;i++,Buffer++) {
241             if((*Buffer) != DOS_QM)
242                 return FALSE;
243         }
244         if(DosOpen)
245             *DosOpen = TRUE;
246         return TRUE;
247     } else
248     if(Name->Length == sizeof(WCHAR)*(3)) {
249         // DOS-style wildcard
250         if(Buffer[0] != DOS_STAR)
251             return FALSE;
252         if(Buffer[1] != DOS_DOT)
253             return FALSE;
254         if(Buffer[2] != DOS_STAR)
255             return FALSE;
256         if(DosOpen)
257             *DosOpen = TRUE;
258         return TRUE;
259     } else {
260         return FALSE;
261     }
262 } // end UDFIsMatchAllMask()
263 
264 BOOLEAN
265 __fastcall
UDFCanNameBeA8dot3(IN PUNICODE_STRING Name)266 UDFCanNameBeA8dot3(
267     IN PUNICODE_STRING Name
268     )
269 {
270     if(Name->Length >= 13 * sizeof(WCHAR))
271         return FALSE;
272 
273     ULONG i,l;
274     ULONG dot_pos=0;
275     ULONG ext_len=0;
276     PWCHAR buff = Name->Buffer;
277 
278     l = Name->Length / sizeof(WCHAR);
279 
280     for(i=0; i<l; i++, buff++) {
281         if( ((*buff) == L'.') ||
282             ((*buff) == DOS_DOT) ) {
283             if(dot_pos)
284                 return FALSE;
285             dot_pos = i+1;
286         } else
287         if(dot_pos) {
288             ext_len++;
289             if(ext_len > 3)
290                 return FALSE;
291         } else
292         if(i >= 8) {
293             return FALSE;
294         }
295     }
296     return TRUE;
297 } // end UDFCanNameBeA8dot3()
298