cb623ae9153cfec50a1ae28a3d2ef5fefed674dd
[openafs.git] / src / WINNT / client_config / drivemap.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 extern "C" {
11 #include <afs/param.h>
12 #include <afs/stds.h>
13 }
14
15 #include <windows.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <WINNT/TaLocale.h>
19 #include "drivemap.h"
20
21
22 /*
23  * REGISTRY ___________________________________________________________________
24  *
25  */
26
27 static const TCHAR AFSConfigKeyName[] = TEXT("SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters");
28
29
30 /*
31  * PROFILE SECTIONS ___________________________________________________________
32  *
33  */
34
35 #define cREALLOC_SUBMOUNTS   4
36
37 static TCHAR cszINIFILE[] = TEXT("afsdsbmt.ini");
38 static TCHAR cszSECTION_SUBMOUNTS[] = TEXT("AFS Submounts");
39 static TCHAR cszSECTION_MAPPINGS[] = TEXT("AFS Mappings");
40
41 static TCHAR cszAUTOSUBMOUNT[] = TEXT("Auto");
42 static TCHAR cszLANMANDEVICE[] = TEXT("\\Device\\LanmanRedirector\\");
43
44
45 /*
46  * STRINGS ____________________________________________________________________
47  *
48  */
49
50 static LPTSTR AllocateStringMemory (size_t cch)
51 {
52    LPTSTR psz = (LPTSTR)Allocate (sizeof(TCHAR) * (cch+1));
53    memset (psz, 0x00, sizeof(TCHAR) * (cch+1));
54    return psz;
55 }
56
57 static void FreeStringMemory (LPTSTR pszString)
58 {
59    Free (pszString);
60 }
61
62 static int lstrncmpi (LPCTSTR pszA, LPCTSTR pszB, size_t cch)
63 {
64    if (!pszA || !pszB)
65       {
66       return (!pszB) - (!pszA);   // A,!B:1, !A,B:-1, !A,!B:0
67       }
68
69    for ( ; cch > 0; cch--, pszA = CharNext(pszA), pszB = CharNext(pszB))
70       {
71       TCHAR chA = toupper( *pszA );
72       TCHAR chB = toupper( *pszB );
73
74       if (!chA || !chB)
75          return (!chB) - (!chA);    // A,!B:1, !A,B:-1, !A,!B:0
76
77       if (chA != chB)
78          return (int)(chA) - (int)(chB);   // -1:A<B, 0:A==B, 1:A>B
79       }
80
81    return 0;  // no differences before told to stop comparing, so A==B
82 }
83
84
85 /*
86  * REALLOC ____________________________________________________________________
87  *
88  */
89
90 #ifndef REALLOC
91 #define REALLOC(_a,_c,_r,_i) DriveMapReallocFunction ((LPVOID*)&_a,sizeof(*_a),&_c,_r,_i)
92 BOOL DriveMapReallocFunction (LPVOID *ppTarget, size_t cbElement, size_t *pcTarget, size_t cReq, size_t cInc)
93 {
94    LPVOID pNew;
95    size_t cNew;
96
97    if (cReq <= *pcTarget)
98       return TRUE;
99
100    if ((cNew = cInc * ((cReq + cInc-1) / cInc)) <= 0)
101       return FALSE;
102
103    if ((pNew = Allocate (cbElement * cNew)) == NULL)
104       return FALSE;
105    memset (pNew, 0x00, cbElement * cNew);
106
107    if (*pcTarget != 0)
108    {
109       memcpy (pNew, *ppTarget, cbElement * (*pcTarget));
110       Free (*ppTarget);
111    }
112
113    *ppTarget = pNew;
114    *pcTarget = cNew;
115    return TRUE;
116 }
117 #endif
118
119
120 /*
121  * WINDOWS NT STUFF ___________________________________________________________
122  *
123  */
124
125 static BOOL IsWindowsNT (void)
126 {
127    static BOOL fChecked = FALSE;
128    static BOOL fIsWinNT = FALSE;
129
130    if (!fChecked)
131       {
132       fChecked = TRUE;
133
134       OSVERSIONINFO Version;
135       memset (&Version, 0x00, sizeof(Version));
136       Version.dwOSVersionInfoSize = sizeof(Version);
137
138       if (GetVersionEx (&Version))
139          {
140          if (Version.dwPlatformId == VER_PLATFORM_WIN32_NT)
141             fIsWinNT = TRUE;
142          }
143       }
144
145    return fIsWinNT;
146 }
147
148
149 /*
150  * GENERAL ____________________________________________________________________
151  *
152  */
153
154 void GetClientNetbiosName (LPTSTR pszName)
155 {
156    *pszName = TEXT('\0');
157
158    if (IsWindowsNT())
159       {
160       HKEY hk;
161       if (RegOpenKey (HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\ComputerName\\ComputerName"), &hk) == 0)
162          {
163          DWORD dwSize = MAX_PATH;
164          DWORD dwType = REG_SZ;
165          RegQueryValueEx (hk, TEXT("ComputerName"), NULL, &dwType, (PBYTE)pszName, &dwSize);
166          }
167       }
168    else // (!IsWindowsNT())
169       {
170       HKEY hk;
171       if (RegOpenKey (HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters"), &hk) == 0)
172          {
173          DWORD dwSize = MAX_PATH;
174          DWORD dwType = REG_SZ;
175          RegQueryValueEx (hk, TEXT("Gateway"), NULL, &dwType, (PBYTE)pszName, &dwSize);
176          }
177       }
178
179    // Shorten the server name from its FQDN
180    //
181    for (LPTSTR pch = pszName; *pch; ++pch)
182       {
183       if (*pch == TEXT('.'))
184          {
185          *(LPTSTR)pch = TEXT('\0');
186          break;
187          }
188       }
189
190    // Form NetBIOS name from client's (possibly truncated) simple host name.
191    if (*pszName != TEXT('\0')) {
192        pszName[11] = TEXT('\0');
193        lstrcat(pszName, TEXT("-AFS"));
194    }
195 }
196
197
198 BOOL SubmountToPath (PDRIVEMAPLIST pList, LPTSTR pszPath, LPTSTR pszSubmount, BOOL fMarkInUse)
199 {
200    // We can't do this translation unless we're under Windows NT.
201    //
202    if (!IsWindowsNT())
203       return FALSE;
204
205    // \\computer-afs\all always maps to "/afs"
206    //
207    if (!lstrcmpi (pszSubmount, TEXT("all")))
208       {
209       lstrcpy (pszPath, TEXT("/afs"));
210       return TRUE;
211       }
212
213    // Otherwise, look up our list of submounts.
214    //
215    for (size_t ii = 0; ii < pList->cSubmounts; ++ii)
216       {
217       if (!lstrcmpi (pList->aSubmounts[ii].szSubmount, pszSubmount))
218          {
219          if (fMarkInUse)
220             pList->aSubmounts[ii].fInUse = TRUE;
221          AdjustAfsPath (pszPath, pList->aSubmounts[ii].szMapping, TRUE, TRUE);
222          return TRUE;
223          }
224       }
225
226    return FALSE;
227 }
228
229
230 BOOL IsValidSubmountName (LPTSTR pszSubmount)
231 {
232    if (!*pszSubmount)
233       return FALSE;
234    if (lstrlen (pszSubmount) > 12)
235       return FALSE;
236
237    for ( ; *pszSubmount; ++pszSubmount)
238       {
239       if (!isprint(*pszSubmount))
240          return FALSE;
241       if (*pszSubmount == TEXT(' '))
242          return FALSE;
243       if (*pszSubmount == TEXT('\t'))
244          return FALSE;
245       }
246
247    return TRUE;
248 }
249
250
251 /*
252  * PIOCTL SUPPORT _____________________________________________________________
253  *
254  */
255
256 extern "C" {
257
258 #include "../afsd/fs_utils.h"
259
260 #define __CM_CONFIG_INTERFACES_ONLY__
261 #include "../afsd/cm_config.h"
262
263 #define __CM_IOCTL_INTERFACES_ONLY__
264 #include "../afsd/cm_ioctl.h"
265
266 } // extern "C"
267
268 #define PIOCTL_MAXSIZE     2048
269
270
271 BOOL fCanIssuePIOCTL (void)
272 {
273    if (!IsWindowsNT())
274       {
275       TCHAR szGateway[ 256 ] = TEXT("");
276       GetClientNetbiosName (szGateway);
277       return (szGateway[0]) ? TRUE : FALSE;
278       }
279
280    SERVICE_STATUS Status;
281    memset (&Status, 0x00, sizeof(Status));
282    Status.dwCurrentState = SERVICE_STOPPED;
283
284    SC_HANDLE hManager;
285    if ((hManager = OpenSCManager (NULL, NULL, GENERIC_READ)) != NULL)
286       {
287       SC_HANDLE hService;
288       if ((hService = OpenService (hManager, TEXT("TransarcAFSDaemon"), GENERIC_READ)) != NULL)
289          {
290          QueryServiceStatus (hService, &Status);
291          CloseServiceHandle (hService);
292          }
293
294       CloseServiceHandle (hManager);
295       }
296
297    return (Status.dwCurrentState == SERVICE_RUNNING) ? TRUE : FALSE;
298 }
299
300
301 /*
302  * QUERYDRIVEMAPLIST __________________________________________________________
303  *
304  */
305
306 void QueryDriveMapList_ReadSubmounts (PDRIVEMAPLIST pList)
307 {
308    if (IsWindowsNT())
309       {
310       size_t cchLHS = 1024;
311       LPTSTR mszLHS = AllocateStringMemory (cchLHS);
312
313       for (int iRetry = 0; iRetry < 5; ++iRetry)
314          {
315          DWORD rc = GetPrivateProfileString (cszSECTION_SUBMOUNTS, NULL, TEXT(""), mszLHS, cchLHS, cszINIFILE);
316          if ((rc != cchLHS-1) && (rc != cchLHS-2))
317             break;
318
319          FreeStringMemory (mszLHS);
320          cchLHS *= 2;
321          mszLHS = AllocateStringMemory (cchLHS);
322          }
323
324       for (LPTSTR psz = mszLHS; psz && *psz; psz += 1+lstrlen(psz))
325          {
326          SUBMOUNT Submount;
327          memset (&Submount, 0x00, sizeof(SUBMOUNT));
328          lstrcpy (Submount.szSubmount, psz);
329
330          TCHAR szMapping[ MAX_PATH ] = TEXT("");
331          GetPrivateProfileString (cszSECTION_SUBMOUNTS, Submount.szSubmount, TEXT(""), szMapping, MAX_PATH, cszINIFILE);
332          if (szMapping[0] != TEXT('\0'))
333             {
334             AdjustAfsPath (Submount.szMapping, szMapping, FALSE, TRUE);
335
336             for (size_t ii = 0; ii < pList->cSubmounts; ++ii)
337                {
338                if (!pList->aSubmounts[ii].szSubmount[0])
339                   break;
340                }
341             if (REALLOC (pList->aSubmounts, pList->cSubmounts, 1+ii, cREALLOC_SUBMOUNTS))
342                {
343                memcpy (&pList->aSubmounts[ii], &Submount, sizeof(SUBMOUNT));
344                }
345             }
346          }
347
348       FreeStringMemory (mszLHS);
349       }
350 }
351
352
353 void QueryDriveMapList_ReadMappings (PDRIVEMAPLIST pList)
354 {
355    size_t cchLHS = 1024;
356    LPTSTR mszLHS = AllocateStringMemory (cchLHS);
357
358    for (int iRetry = 0; iRetry < 5; ++iRetry)
359       {
360       DWORD rc = GetPrivateProfileString (cszSECTION_MAPPINGS, NULL, TEXT(""), mszLHS, cchLHS, cszINIFILE);
361       if ((rc != cchLHS-1) && (rc != cchLHS-2))
362          break;
363
364       FreeStringMemory (mszLHS);
365       cchLHS *= 2;
366       mszLHS = AllocateStringMemory (cchLHS);
367       }
368
369    for (LPTSTR psz = mszLHS; psz && *psz; psz += 1+lstrlen(psz))
370       {
371       DRIVEMAP DriveMap;
372       memset (&DriveMap, 0x00, sizeof(DRIVEMAP));
373       DriveMap.chDrive = toupper(*psz);
374       DriveMap.fPersistent = TRUE;
375       if ((DriveMap.chDrive < chDRIVE_A) || (DriveMap.chDrive > chDRIVE_Z))
376          continue;
377
378       TCHAR szMapping[ MAX_PATH ] = TEXT("");
379       GetPrivateProfileString (cszSECTION_MAPPINGS, psz, TEXT(""), szMapping, MAX_PATH, cszINIFILE);
380       if (szMapping[0] != TEXT('\0'))
381          {
382          AdjustAfsPath (DriveMap.szMapping, szMapping, TRUE, TRUE);
383          if (DriveMap.szMapping[ lstrlen(DriveMap.szMapping)-1 ] == TEXT('*'))
384             {
385             DriveMap.fPersistent = FALSE;
386             DriveMap.szMapping[ lstrlen(DriveMap.szMapping)-1 ] = TEXT('\0');
387             }
388          size_t iDrive = DriveMap.chDrive - chDRIVE_A;
389          memcpy (&pList->aDriveMap[ iDrive ], &DriveMap, sizeof(DRIVEMAP));
390          }
391       }
392
393    FreeStringMemory (mszLHS);
394 }
395
396
397 void QueryDriveMapList_WriteMappings (PDRIVEMAPLIST pList)
398 {
399    WriteDriveMappings (pList);
400 }
401
402
403 void WriteDriveMappings (PDRIVEMAPLIST pList)
404 {
405    WritePrivateProfileString (cszSECTION_MAPPINGS, NULL, NULL, cszINIFILE);
406
407    for (size_t iDrive = 0; iDrive < 26; ++iDrive)
408       {
409       if (pList->aDriveMap[iDrive].szMapping[0] != TEXT('\0'))
410          {
411          TCHAR szLHS[] = TEXT("*");
412          szLHS[0] = pList->aDriveMap[iDrive].chDrive;
413
414          TCHAR szRHS[MAX_PATH];
415          AdjustAfsPath (szRHS, pList->aDriveMap[iDrive].szMapping, TRUE, TRUE);
416          if (!pList->aDriveMap[iDrive].fPersistent)
417             lstrcat (szRHS, TEXT("*"));
418
419          WritePrivateProfileString (cszSECTION_MAPPINGS, szLHS, szRHS, cszINIFILE);
420          }
421       }
422 }
423
424 BOOL DriveIsGlobalAfsDrive(TCHAR chDrive)
425 {
426    TCHAR szKeyName[128];
427    TCHAR szValueName[128];
428    TCHAR szValue[128];
429    HKEY hKey;
430
431    _stprintf(szKeyName, TEXT("%s\\GlobalAutoMapper"), AFSConfigKeyName);
432
433    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS)
434       return FALSE;
435
436    _stprintf(szValueName, TEXT("%c:"), chDrive);
437
438    DWORD dwSize = sizeof(szValue);
439    BOOL bIsGlobal = (RegQueryValueEx (hKey, szValueName, NULL, NULL, (PBYTE)szValue, &dwSize) == ERROR_SUCCESS);
440
441    RegCloseKey (hKey);
442    
443    return bIsGlobal;
444 }
445
446
447 void QueryDriveMapList_FindNetworkDrives (PDRIVEMAPLIST pList, BOOL *pfFoundNew)
448 {
449    for (TCHAR chDrive = chDRIVE_A; chDrive <= chDRIVE_Z; ++chDrive)
450       {
451       TCHAR szSubmount[ MAX_PATH ];
452       if (!GetDriveSubmount (chDrive, szSubmount))
453          continue;
454
455       // We've got a mapping!  Drive {chDrive} is mapped to submount
456       // {szSubmount}. See if that submount makes sense.
457       //
458       if (!IsWindowsNT())
459          {
460          size_t iDrive = chDrive - chDRIVE_A;
461          if (pList->aDriveMap[ iDrive ].szMapping[0] != TEXT('\0'))
462             {
463             pList->aDriveMap[ iDrive ].fActive = TRUE;
464             lstrcpy (pList->aDriveMap[ iDrive ].szSubmount, szSubmount);
465             }
466          continue;
467          }
468       else // (IsWindowsNT())
469          {
470          TCHAR szAfsPath[ MAX_PATH ];
471          if (!SubmountToPath (pList, szAfsPath, szSubmount, TRUE))
472             continue;
473
474          // Okay, we know that drive {chDrive} is mapped to afs path {szAfsPath}.
475          // If this drive is a global afs drive, then reject it.  Otherwise, look 
476          // at pList->aDriveMap, to see if this drive mapping is already in our 
477          // list. If not, add it and set pfFoundNew.
478          //
479          if (DriveIsGlobalAfsDrive(chDrive))
480             continue;
481          
482          size_t iDrive = chDrive - chDRIVE_A;
483          if (lstrcmpi (pList->aDriveMap[ iDrive ].szMapping, szAfsPath))
484             {
485             *pfFoundNew = TRUE;
486             pList->aDriveMap[ iDrive ].fPersistent = TRUE;
487             }
488          pList->aDriveMap[ iDrive ].fActive = TRUE;
489          pList->aDriveMap[ iDrive ].chDrive = chDrive;
490          lstrcpy (pList->aDriveMap[ iDrive ].szSubmount, szSubmount);
491          AdjustAfsPath (pList->aDriveMap[ iDrive ].szMapping, szAfsPath, TRUE, TRUE);
492          }
493       }
494 }
495
496
497 void QueryDriveMapList (PDRIVEMAPLIST pList)
498 {
499    // Initialize the data structure
500    //
501    memset (pList, 0x00, sizeof(DRIVEMAPLIST));
502    for (size_t ii = 0; ii < 26; ++ii)
503       pList->aDriveMap[ii].chDrive = chDRIVE_A + ii;
504
505    // Read the current lists of submounts and drive letter mappings
506    //
507    QueryDriveMapList_ReadSubmounts (pList);
508    QueryDriveMapList_ReadMappings (pList);
509
510    // Look through the current list of network drives, and see if
511    // any are currently mapped to AFS. If we find any which are mapped
512    // into AFS unexpectedly, we'll have to rewrite the mappings list.
513    //
514    BOOL fFoundNew = FALSE;
515    QueryDriveMapList_FindNetworkDrives (pList, &fFoundNew);
516
517    if (fFoundNew)
518       {
519       QueryDriveMapList_WriteMappings (pList);
520       }
521 }
522
523
524 void FreeDriveMapList (PDRIVEMAPLIST pList)
525 {
526    if (pList->aSubmounts)
527       Free (pList->aSubmounts);
528    memset (pList, 0x00, sizeof(DRIVEMAPLIST));
529 }
530
531
532 BOOL PathToSubmount (LPTSTR pszSubmount, LPTSTR pszMapping, LPTSTR pszSubmountReq, ULONG *pStatus)
533 {
534    if (pszSubmountReq && !IsValidSubmountName (pszSubmountReq))
535       pszSubmountReq = NULL;
536
537    TCHAR szAfsPath[ MAX_PATH ];
538    AdjustAfsPath (szAfsPath, pszMapping, TRUE, TRUE);
539
540    // Try to ask AFSD for a new submount name.
541    //
542    if (!fCanIssuePIOCTL())
543       return FALSE;
544
545    BYTE InData[ PIOCTL_MAXSIZE ];
546    memset (InData, 0x00, sizeof(InData));
547
548    LPTSTR pszInData = (LPTSTR)InData;
549    lstrcpy (pszInData, pszMapping);
550    pszInData += 1+lstrlen(pszInData);
551    if (pszSubmountReq)
552       lstrcpy (pszInData, pszSubmountReq);
553
554    BYTE OutData[ PIOCTL_MAXSIZE ];
555    memset (OutData, 0x00, sizeof(OutData));
556
557    struct ViceIoctl IOInfo;
558    IOInfo.in = (char *)InData;
559    IOInfo.in_size = PIOCTL_MAXSIZE;
560    IOInfo.out = (char *)OutData;
561    IOInfo.out_size = PIOCTL_MAXSIZE;
562
563    ULONG status;
564    if ((status = pioctl (0, VIOC_MAKESUBMOUNT, &IOInfo, 1)) != 0)
565       return FALSE;
566
567    lstrcpy (pszSubmount, (LPCTSTR)OutData);
568    return (pszSubmount[0] != TEXT('\0')) ? TRUE : FALSE;
569 }
570
571
572 BOOL ActivateDriveMap (TCHAR chDrive, LPTSTR pszMapping, LPTSTR pszSubmountReq, BOOL fPersistent, DWORD *pdwStatus)
573 {
574    // We can only map drives to places in AFS using this function.
575    //
576    if ( (lstrncmpi (pszMapping, TEXT("/afs"), lstrlen(TEXT("/afs")))) &&
577         (lstrncmpi (pszMapping, TEXT("\\afs"), lstrlen(TEXT("\\afs")))) )
578       {
579       if (pdwStatus)
580          *pdwStatus = ERROR_BAD_NETPATH;
581       return FALSE;
582       }
583
584    // First we have to translate {pszMapping} into a submount, and if there is
585    // no current submount associated with this path, we'll have to make one.
586    //
587    ULONG status;
588    TCHAR szSubmount[ MAX_PATH ];
589    if (!PathToSubmount (szSubmount, pszMapping, pszSubmountReq, &status))
590       {
591       if (pdwStatus)
592          *pdwStatus = status;
593       return FALSE;
594       }
595
596    // We now have a submount name and drive letter--map the network drive.
597    //
598    TCHAR szClient[ MAX_PATH ];
599    GetClientNetbiosName (szClient);
600
601    TCHAR szLocal[ MAX_PATH ] = TEXT("*:");
602    szLocal[0] = chDrive;
603
604    TCHAR szRemote[ MAX_PATH ];
605    wsprintf (szRemote, TEXT("\\\\%s\\%s"), szClient, szSubmount);
606
607    NETRESOURCE Resource;
608    memset (&Resource, 0x00, sizeof(NETRESOURCE));
609    Resource.dwScope = RESOURCE_GLOBALNET;
610    Resource.dwType = RESOURCETYPE_DISK;
611    Resource.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
612    Resource.dwUsage = RESOURCEUSAGE_CONNECTABLE;
613    Resource.lpLocalName = szLocal;
614    Resource.lpRemoteName = szRemote;
615
616    DWORD rc = WNetAddConnection2 (&Resource, NULL, NULL, ((fPersistent) ? CONNECT_UPDATE_PROFILE : 0));
617    if (rc == NO_ERROR)
618       return TRUE;
619
620    if (pdwStatus)
621       *pdwStatus = rc;
622    return FALSE;
623 }
624
625
626 BOOL InactivateDriveMap (TCHAR chDrive, DWORD *pdwStatus)
627 {
628    TCHAR szLocal[ MAX_PATH ] = TEXT("*:");
629    szLocal[0] = chDrive;
630
631    DWORD rc = WNetCancelConnection (szLocal, FALSE);
632    if (rc == NO_ERROR)
633       return TRUE;
634
635    if (pdwStatus)
636       *pdwStatus = rc;
637    return FALSE;
638 }
639
640
641 void AddSubMount (LPTSTR pszSubmount, LPTSTR pszMapping)
642 {
643    TCHAR szRHS[ MAX_PATH ];
644    AdjustAfsPath (szRHS, pszMapping, FALSE, TRUE);
645    if (!szRHS[0])
646       lstrcpy (szRHS, TEXT("/"));
647    WritePrivateProfileString (cszSECTION_SUBMOUNTS, pszSubmount, szRHS, cszINIFILE);
648 }
649
650
651 void RemoveSubMount (LPTSTR pszSubmount)
652 {
653    WritePrivateProfileString (cszSECTION_SUBMOUNTS, pszSubmount, NULL, cszINIFILE);
654 }
655
656
657 void AdjustAfsPath (LPTSTR pszTarget, LPCTSTR pszSource, BOOL fWantAFS, BOOL fWantForwardSlashes)
658 {
659    if (!*pszSource)
660       lstrcpy (pszTarget, (fWantAFS) ? TEXT("/afs") : TEXT(""));
661    else if ((*pszSource != TEXT('/')) && (*pszSource != TEXT('\\')))
662       wsprintf (pszTarget, TEXT("/afs/%s"), pszSource);
663    else if (fWantAFS && lstrncmpi (&pszSource[1], TEXT("afs"), 3))
664       wsprintf (pszTarget, TEXT("/afs%s"), pszSource);
665    else if (!fWantAFS && !lstrncmpi (&pszSource[1], TEXT("afs"), 3))
666       lstrcpy (pszTarget, &pszSource[4]);
667    else
668       lstrcpy (pszTarget, pszSource);
669
670    for (LPTSTR pch = pszTarget; *pch; ++pch)
671       {
672       if (fWantForwardSlashes)
673          {
674          *pch = (*pch == TEXT('\\')) ? TEXT('/') : (*pch);
675          }
676       else // (!fWantForwardSlashes)
677          {
678          *pch = (*pch == TEXT('/')) ? TEXT('\\') : (*pch);
679          }
680       }
681
682    if (lstrlen(pszTarget) &&
683        ((pszTarget[lstrlen(pszTarget)-1] == TEXT('/')) ||
684         (pszTarget[lstrlen(pszTarget)-1] == TEXT('\\'))))
685       {
686       pszTarget[lstrlen(pszTarget)-1] = TEXT('\0');
687       }
688 }
689
690
691 BOOL GetDriveSubmount (TCHAR chDrive, LPTSTR pszSubmountNow)
692 {
693    TCHAR szDrive[] = TEXT("*:");
694    szDrive[0] = chDrive;
695
696    TCHAR szMapping[ MAX_PATH ] = TEXT("");
697    LPTSTR pszSubmount = szMapping;
698
699    if (IsWindowsNT())
700       {
701       QueryDosDevice (szDrive, szMapping, MAX_PATH);
702
703       // Now if this is an AFS network drive mapping, {szMapping} will be:
704       //
705       //   \Device\LanmanRedirector\Q:\machine-afs\submount
706       //
707       if (lstrncmpi (szMapping, cszLANMANDEVICE, lstrlen(cszLANMANDEVICE)))
708          return FALSE;
709       pszSubmount = &szMapping[ lstrlen(cszLANMANDEVICE) ];
710       if (toupper(*pszSubmount) != chDrive)
711          return FALSE;
712       if (*(++pszSubmount) != TEXT(':'))
713          return FALSE;
714       if (*(++pszSubmount) != TEXT('\\'))
715          return FALSE;
716       for (++pszSubmount; *pszSubmount && (*pszSubmount != TEXT('\\')); ++pszSubmount)
717          if (!lstrncmpi (pszSubmount, TEXT("-afs\\"), lstrlen(TEXT("-afs\\"))))
718             break;
719       if ((!*pszSubmount) || (*pszSubmount == TEXT('\\')))
720          return FALSE;
721       pszSubmount += lstrlen("-afs\\");
722       }
723    else // (!IsWindowsNT())
724       {
725       DWORD dwSize = MAX_PATH;
726       if (WNetGetConnection (szDrive, szMapping, &dwSize) != NO_ERROR)
727          return FALSE;
728       if (*(pszSubmount++) != TEXT('\\'))
729          return FALSE;
730       if (*(pszSubmount++) != TEXT('\\'))
731          return FALSE;
732       for ( ; *pszSubmount && (*pszSubmount != TEXT('\\')); ++pszSubmount)
733          if (!lstrncmpi (pszSubmount, TEXT("-afs\\"), lstrlen(TEXT("-afs\\"))))
734             break;
735       if ((!*pszSubmount) || (*pszSubmount == TEXT('\\')))
736          return FALSE;
737       pszSubmount += lstrlen("-afs\\");
738       }
739
740    if (!pszSubmount || !*pszSubmount)
741       return FALSE;
742
743    lstrcpy (pszSubmountNow, pszSubmount);
744    return TRUE;
745 }
746