Windows: pioctl use GetFileAttributes
[openafs.git] / src / sys / pioctl_nt.c
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 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #include <windows.h>
17 #include <winioctl.h>
18 #define SECURITY_WIN32
19 #include <security.h>
20 #include <nb30.h>
21 #include <tchar.h>
22 #include <strsafe.h>
23
24 #include <osi.h>
25
26 #include <cm.h>
27 #include <cm_nls.h>
28 #include <cm_server.h>
29 #include <cm_cell.h>
30 #include <cm_user.h>
31 #include <cm_conn.h>
32 #include <cm_scache.h>
33 #include <cm_buf.h>
34 #include <cm_dir.h>
35 #include <cm_utils.h>
36 #include <cm_ioctl.h>
37 #include <smb_iocons.h>
38 #include <smb.h>
39 #include <pioctl_nt.h>
40 #include <WINNT/afsreg.h>
41 #include <lanahelper.h>
42
43 #include <krb5.h>
44 #include <..\WINNT\afsrdr\common\AFSUserDefines.h>
45 #include <..\WINNT\afsrdr\common\AFSUserIoctl.h>
46 #include <..\WINNT\afsrdr\common\AFSUserStructs.h>
47
48 static char AFSConfigKeyName[] = AFSREG_CLT_SVC_PARAM_SUBKEY;
49
50 static const char utf8_prefix[] = UTF8_PREFIX;
51 static const int  utf8_prefix_size = sizeof(utf8_prefix) -  sizeof(char);
52
53 #define FS_IOCTLREQUEST_MAXSIZE 8192
54 /* big structure for representing and storing an IOCTL request */
55 typedef struct fs_ioctlRequest {
56     char *mp;                   /* marshalling/unmarshalling ptr */
57     long nbytes;                /* bytes received (when unmarshalling) */
58     char data[FS_IOCTLREQUEST_MAXSIZE]; /* data we're marshalling */
59 } fs_ioctlRequest_t;
60
61
62 static int
63 CMtoUNIXerror(int cm_code)
64 {
65     switch (cm_code) {
66     case CM_ERROR_TIMEDOUT:
67         return ETIMEDOUT;
68     case CM_ERROR_NOACCESS:
69         return EACCES;
70     case CM_ERROR_NOSUCHFILE:
71     case CM_ERROR_NOSUCHPATH:
72     case CM_ERROR_BPLUS_NOMATCH:
73         return ENOENT;
74     case CM_ERROR_INVAL:
75         return EINVAL;
76     case CM_ERROR_BADFD:
77         return EBADF;
78     case CM_ERROR_EXISTS:
79     case CM_ERROR_INEXACT_MATCH:
80         return EEXIST;
81     case CM_ERROR_CROSSDEVLINK:
82         return EXDEV;
83     case CM_ERROR_NOTDIR:
84         return ENOTDIR;
85     case CM_ERROR_ISDIR:
86         return EISDIR;
87     case CM_ERROR_READONLY:
88         return EROFS;
89     case CM_ERROR_WOULDBLOCK:
90         return EWOULDBLOCK;
91     case CM_ERROR_NOSUCHCELL:
92         return ESRCH;           /* hack */
93     case CM_ERROR_NOSUCHVOLUME:
94         return EPIPE;           /* hack */
95     case CM_ERROR_NOMORETOKENS:
96         return EDOM;            /* hack */
97     case CM_ERROR_TOOMANYBUFS:
98         return EFBIG;           /* hack */
99     case CM_ERROR_ALLBUSY:
100         return EBUSY;
101     case CM_ERROR_ALLDOWN:
102         return ENOSYS;          /* hack */
103     case CM_ERROR_ALLOFFLINE:
104         return ENXIO;           /* hack */
105     default:
106         if (cm_code > 0 && cm_code < EILSEQ)
107             return cm_code;
108         else
109             return ENOTTY;
110     }
111 }
112
113 static void
114 InitFSRequest(fs_ioctlRequest_t * rp)
115 {
116     rp->mp = rp->data;
117     rp->nbytes = 0;
118 }
119
120 static BOOL
121 IoctlDebug(void)
122 {
123     static int init = 0;
124     static BOOL debug = 0;
125
126     if ( !init ) {
127         HKEY hk;
128
129         if (RegOpenKey (HKEY_LOCAL_MACHINE,
130                          TEXT("Software\\OpenAFS\\Client"), &hk) == 0)
131         {
132             DWORD dwSize = sizeof(BOOL);
133             DWORD dwType = REG_DWORD;
134             RegQueryValueEx (hk, TEXT("IoctlDebug"), NULL, &dwType, (PBYTE)&debug, &dwSize);
135             RegCloseKey (hk);
136         }
137
138         init = 1;
139     }
140
141     return debug;
142 }
143
144 static BOOL
145 RDR_Ready(void)
146 {
147     HANDLE hDevHandle = NULL;
148     DWORD bytesReturned;
149     AFSDriverStatusRespCB *respBuffer = NULL;
150     DWORD rc = 0;
151     BOOL ready = FALSE;
152
153     hDevHandle = CreateFileW( AFS_SYMLINK_W,
154                              GENERIC_READ | GENERIC_WRITE,
155                              FILE_SHARE_READ | FILE_SHARE_WRITE,
156                              NULL,
157                              OPEN_EXISTING,
158                              0,
159                              NULL);
160     if( hDevHandle == INVALID_HANDLE_VALUE)
161     {
162         DWORD gle = GetLastError();
163
164         if (gle && IoctlDebug() ) {
165             char buf[4096];
166             int saveerrno;
167
168             saveerrno = errno;
169             if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
170                                NULL,
171                                gle,
172                                MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
173                                buf,
174                                4096,
175                                (va_list *) NULL
176                                ) )
177             {
178                 fprintf(stderr,"RDR_Ready CreateFile(%S) failed: 0x%X\r\n\t[%s]\r\n",
179                         AFS_SYMLINK_W,gle,buf);
180             }
181             errno = saveerrno;
182         }
183         return FALSE;
184     }
185
186     //
187     // Allocate a response buffer.
188     //
189     respBuffer = (AFSDriverStatusRespCB *)malloc( sizeof( AFSDriverStatusRespCB));
190     if( respBuffer)
191     {
192
193         memset( respBuffer, '\0', sizeof( AFSDriverStatusRespCB));
194
195         if( !DeviceIoControl( hDevHandle,
196                               IOCTL_AFS_STATUS_REQUEST,
197                               NULL,
198                               0,
199                               (void *)respBuffer,
200                               sizeof( AFSDriverStatusRespCB),
201                               &bytesReturned,
202                               NULL))
203         {
204             //
205             // Error condition back from driver
206             //
207             DWORD gle = GetLastError();
208
209             if (gle && IoctlDebug() ) {
210                 char buf[4096];
211                 int saveerrno;
212
213                 saveerrno = errno;
214                 if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
215                                     NULL,
216                                     gle,
217                                     MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
218                                     buf,
219                                     4096,
220                                     (va_list *) NULL
221                                     ) )
222                 {
223                     fprintf(stderr,"RDR_Ready CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
224                              AFS_SYMLINK,gle,buf);
225                 }
226                 errno = saveerrno;
227             }
228             rc = -1;
229             goto cleanup;
230         }
231
232         if (bytesReturned == sizeof(AFSDriverStatusRespCB))
233         {
234             ready = ( respBuffer->Status == AFS_DRIVER_STATUS_READY );
235         }
236     } else
237         rc = ENOMEM;
238
239   cleanup:
240     if (respBuffer)
241         free( respBuffer);
242
243     if (hDevHandle != INVALID_HANDLE_VALUE)
244         CloseHandle(hDevHandle);
245
246     return ready;
247 }
248
249 static BOOL
250 DisableServiceManagerCheck(void)
251 {
252     static int init = 0;
253     static BOOL smcheck = 0;
254
255     if ( !init ) {
256         HKEY hk;
257
258         if (RegOpenKey (HKEY_LOCAL_MACHINE,
259                          TEXT("Software\\OpenAFS\\Client"), &hk) == 0)
260         {
261             DWORD dwSize = sizeof(BOOL);
262             DWORD dwType = REG_DWORD;
263             RegQueryValueEx (hk, TEXT("DisableIoctlSMCheck"), NULL, &dwType, (PBYTE)&smcheck, &dwSize);
264             RegCloseKey (hk);
265         }
266
267         init = 1;
268     }
269
270     return smcheck;
271 }
272
273 static DWORD
274 GetServiceStatus(
275     LPSTR lpszMachineName,
276     LPSTR lpszServiceName,
277     DWORD *lpdwCurrentState)
278 {
279     DWORD           hr               = NOERROR;
280     SC_HANDLE       schSCManager     = NULL;
281     SC_HANDLE       schService       = NULL;
282     DWORD           fdwDesiredAccess = 0;
283     SERVICE_STATUS  ssServiceStatus  = {0};
284     BOOL            fRet             = FALSE;
285
286     *lpdwCurrentState = 0;
287
288     fdwDesiredAccess = GENERIC_READ;
289
290     schSCManager = OpenSCManager(lpszMachineName,
291                                  NULL,
292                                  fdwDesiredAccess);
293
294     if(schSCManager == NULL)
295     {
296         hr = GetLastError();
297         goto cleanup;
298     }
299
300     schService = OpenService(schSCManager,
301                              lpszServiceName,
302                              fdwDesiredAccess);
303
304     if(schService == NULL)
305     {
306         hr = GetLastError();
307         goto cleanup;
308     }
309
310     fRet = QueryServiceStatus(schService,
311                               &ssServiceStatus);
312
313     if(fRet == FALSE)
314     {
315         hr = GetLastError();
316         goto cleanup;
317     }
318
319     *lpdwCurrentState = ssServiceStatus.dwCurrentState;
320
321 cleanup:
322
323     CloseServiceHandle(schService);
324     CloseServiceHandle(schSCManager);
325
326     return(hr);
327 }
328
329 static BOOL
330 UnicodeToANSI(LPCWSTR lpInputString, LPSTR lpszOutputString, int nOutStringLen)
331 {
332     CPINFO CodePageInfo;
333
334     GetCPInfo(CP_ACP, &CodePageInfo);
335
336     if (CodePageInfo.MaxCharSize > 1) {
337         // Only supporting non-Unicode strings
338         int reqLen = WideCharToMultiByte( CP_ACP, 0,
339                                           lpInputString, -1,
340                                           NULL, 0, NULL, NULL);
341         if ( reqLen > nOutStringLen)
342         {
343             return FALSE;
344         } else {
345             if (WideCharToMultiByte( CP_ACP,
346                                      WC_COMPOSITECHECK,
347                                      lpInputString, -1,
348                                      lpszOutputString,
349                                      nOutStringLen, NULL, NULL) == 0)
350                 return FALSE;
351         }
352     }
353     else
354     {
355         // Looks like unicode, better translate it
356         if (WideCharToMultiByte( CP_ACP,
357                                  WC_COMPOSITECHECK,
358                                  lpInputString, -1,
359                                  lpszOutputString,
360                                  nOutStringLen, NULL, NULL) == 0)
361             return FALSE;
362     }
363
364     return TRUE;
365 }
366
367 static BOOL
368 GetLSAPrincipalName(char * pszUser, DWORD dwUserSize)
369 {
370     KERB_QUERY_TKT_CACHE_REQUEST CacheRequest;
371     PKERB_RETRIEVE_TKT_RESPONSE pTicketResponse = NULL;
372     ULONG ResponseSize;
373     PKERB_EXTERNAL_NAME pClientName = NULL;
374     PUNICODE_STRING     pDomainName = NULL;
375     LSA_STRING Name;
376     HANDLE hLogon = INVALID_HANDLE_VALUE;
377     ULONG PackageId;
378     NTSTATUS ntStatus;
379     NTSTATUS ntSubStatus = 0;
380     WCHAR * wchUser = NULL;
381     DWORD   dwSize;
382     SHORT   sCount;
383     BOOL bRet = FALSE;
384
385     ntStatus = LsaConnectUntrusted( &hLogon);
386     if (FAILED(ntStatus))
387         goto cleanup;
388
389     Name.Buffer = MICROSOFT_KERBEROS_NAME_A;
390     Name.Length = (USHORT)(sizeof(MICROSOFT_KERBEROS_NAME_A) - sizeof(char));
391     Name.MaximumLength = Name.Length;
392
393     ntStatus = LsaLookupAuthenticationPackage( hLogon, &Name, &PackageId);
394     if (FAILED(ntStatus))
395         goto cleanup;
396
397     memset(&CacheRequest, 0, sizeof(KERB_QUERY_TKT_CACHE_REQUEST));
398     CacheRequest.MessageType = KerbRetrieveTicketMessage;
399     CacheRequest.LogonId.LowPart = 0;
400     CacheRequest.LogonId.HighPart = 0;
401
402     ntStatus = LsaCallAuthenticationPackage( hLogon,
403                                              PackageId,
404                                              &CacheRequest,
405                                              sizeof(CacheRequest),
406                                              &pTicketResponse,
407                                              &ResponseSize,
408                                              &ntSubStatus);
409     if (FAILED(ntStatus) || FAILED(ntSubStatus))
410         goto cleanup;
411
412     /* We have a ticket in the response */
413     pClientName = pTicketResponse->Ticket.ClientName;
414     pDomainName = &pTicketResponse->Ticket.DomainName;
415
416     /* We want to return ClientName @ DomainName */
417
418     dwSize = 0;
419     for ( sCount = 0; sCount < pClientName->NameCount; sCount++)
420     {
421         dwSize += pClientName->Names[sCount].Length;
422     }
423     dwSize += pDomainName->Length + sizeof(WCHAR);
424
425     if ( dwSize / sizeof(WCHAR) > dwUserSize )
426         goto cleanup;
427
428     wchUser = malloc(dwSize);
429     if (wchUser == NULL)
430         goto cleanup;
431
432     for ( sCount = 0, wchUser[0] = L'\0'; sCount < pClientName->NameCount; sCount++)
433     {
434         StringCbCatNW( wchUser, dwSize,
435                        pClientName->Names[sCount].Buffer,
436                        pClientName->Names[sCount].Length);
437     }
438     StringCbCatNW( wchUser, dwSize,
439                    pDomainName->Buffer,
440                    pDomainName->Length);
441
442     if ( !UnicodeToANSI( wchUser, pszUser, dwUserSize) )
443         goto cleanup;
444
445     bRet = TRUE;
446
447   cleanup:
448
449     if (wchUser)
450         free(wchUser);
451
452     if ( hLogon != INVALID_HANDLE_VALUE)
453         LsaDeregisterLogonProcess(hLogon);
454
455     if ( pTicketResponse ) {
456         SecureZeroMemory(pTicketResponse,ResponseSize);
457         LsaFreeReturnBuffer(pTicketResponse);
458     }
459
460     return bRet;
461 }
462
463 //
464 // Recursively evaluate drivestr to find the final
465 // dos drive letter to which the source is mapped.
466 //
467 static BOOL
468 DriveSubstitution(char *drivestr, char *subststr, size_t substlen)
469 {
470     char device[MAX_PATH];
471
472     if ( QueryDosDevice(drivestr, device, MAX_PATH) )
473     {
474         if ( device[0] == '\\' &&
475              device[1] == '?' &&
476              device[2] == '?' &&
477              device[3] == '\\' &&
478              isalpha(device[4]) &&
479              device[5] == ':')
480         {
481             device[0] = device[4];
482             device[1] = ':';
483             device[2] = '\0';
484             if ( DriveSubstitution(device, subststr, substlen) )
485             {
486                 return TRUE;
487             } else {
488                 subststr[0] = device[0];
489                 subststr[1] = ':';
490                 subststr[2] = '\0';
491                 return TRUE;
492             }
493         } else
494         if ( device[0] == '\\' &&
495              device[1] == '?' &&
496              device[2] == '?' &&
497              device[3] == '\\' &&
498              device[4] == 'U' &&
499              device[5] == 'N' &&
500              device[6] == 'C' &&
501              device[7] == '\\')
502         {
503              subststr[0] = '\\';
504              strncpy(&subststr[1], &device[7], substlen-1);
505              subststr[substlen-1] = '\0';
506              return TRUE;
507         }
508     }
509
510     return FALSE;
511 }
512
513 //
514 // drivestr - is "<drive-letter>:"
515 //
516 static BOOL
517 DriveIsMappedToAFS(char *drivestr, char *NetbiosName)
518 {
519     DWORD dwResult, dwResultEnum;
520     HANDLE hEnum;
521     DWORD cbBuffer = 16384;     // 16K is a good size
522     DWORD cEntries = -1;        // enumerate all possible entries
523     LPNETRESOURCE lpnrLocal;    // pointer to enumerated structures
524     DWORD i;
525     BOOL  bIsAFS = FALSE;
526     char  subststr[MAX_PATH];
527     char  device[MAX_PATH];
528
529     //
530     // Handle drive letter substitution created with "SUBST <drive> <path>".
531     // If a substitution has occurred, use the target drive letter instead
532     // of the source.
533     //
534     if ( DriveSubstitution(drivestr, subststr, MAX_PATH) )
535     {
536         if (subststr[0] == '\\' &&
537             subststr[1] == '\\')
538         {
539             if (_strnicmp( &subststr[2], NetbiosName, strlen(NetbiosName)) == 0)
540                 return TRUE;
541             else
542                 return FALSE;
543         }
544         drivestr = subststr;
545     }
546
547     //
548     // Check for \Device\AFSRedirector
549     //
550     if (QueryDosDevice(drivestr, device, MAX_PATH) &&
551         _strnicmp( device, "\\Device\\AFSRedirector", strlen("\\Device\\AFSRedirector")) == 0) {
552         return TRUE;
553     }
554
555     //
556     // Call the WNetOpenEnum function to begin the enumeration.
557     //
558     dwResult = WNetOpenEnum(RESOURCE_CONNECTED,
559                             RESOURCETYPE_DISK,
560                             RESOURCEUSAGE_ALL,
561                             NULL,       // NULL first time the function is called
562                             &hEnum);    // handle to the resource
563
564     if (dwResult != NO_ERROR)
565         return FALSE;
566
567     //
568     // Call the GlobalAlloc function to allocate resources.
569     //
570     lpnrLocal = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer);
571     if (lpnrLocal == NULL)
572         return FALSE;
573
574     do {
575         //
576         // Initialize the buffer.
577         //
578         ZeroMemory(lpnrLocal, cbBuffer);
579         //
580         // Call the WNetEnumResource function to continue
581         //  the enumeration.
582         //
583         cEntries = -1;
584         dwResultEnum = WNetEnumResource(hEnum,          // resource handle
585                                         &cEntries,      // defined locally as -1
586                                         lpnrLocal,      // LPNETRESOURCE
587                                         &cbBuffer);     // buffer size
588         //
589         // If the call succeeds, loop through the structures.
590         //
591         if (dwResultEnum == NO_ERROR) {
592             for (i = 0; i < cEntries; i++) {
593                 if (lpnrLocal[i].lpLocalName &&
594                     toupper(lpnrLocal[i].lpLocalName[0]) == toupper(drivestr[0])) {
595                     //
596                     // Skip the two backslashes at the start of the UNC device name
597                     //
598                     if ( _strnicmp( &(lpnrLocal[i].lpRemoteName[2]), NetbiosName, strlen(NetbiosName)) == 0 )
599                     {
600                         bIsAFS = TRUE;
601                         break;
602                     }
603                 }
604             }
605         }
606         // Process errors.
607         //
608         else if (dwResultEnum != ERROR_NO_MORE_ITEMS)
609             break;
610     }
611     while (dwResultEnum != ERROR_NO_MORE_ITEMS);
612
613     //
614     // Call the GlobalFree function to free the memory.
615     //
616     GlobalFree((HGLOBAL) lpnrLocal);
617     //
618     // Call WNetCloseEnum to end the enumeration.
619     //
620     dwResult = WNetCloseEnum(hEnum);
621
622     return bIsAFS;
623 }
624
625 static BOOL
626 DriveIsGlobalAutoMapped(char *drivestr)
627 {
628     DWORD dwResult;
629     HKEY hKey;
630     DWORD dwSubMountSize;
631     char szSubMount[260];
632     DWORD dwType;
633
634     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
635                             AFSREG_CLT_SVC_PARAM_SUBKEY "\\GlobalAutoMapper",
636                             0, KEY_QUERY_VALUE, &hKey);
637     if (dwResult != ERROR_SUCCESS)
638         return FALSE;
639
640     dwSubMountSize = sizeof(szSubMount);
641     dwType = REG_SZ;
642     dwResult = RegQueryValueEx(hKey, drivestr, 0, &dwType, szSubMount, &dwSubMountSize);
643     RegCloseKey(hKey);
644
645     if (dwResult == ERROR_SUCCESS && dwType == REG_SZ)
646         return TRUE;
647     else
648         return FALSE;
649 }
650
651 static long
652 GetIoctlHandle(char *fileNamep, HANDLE * handlep)
653 {
654     HKEY hk;
655     char *drivep = NULL;
656     char netbiosName[MAX_NB_NAME_LENGTH]="AFS";
657     DWORD CurrentState = 0;
658     char  HostName[64] = "";
659     char tbuffer[MAX_PATH]="";
660     HANDLE fh;
661     char szUser[128] = "";
662     char szClient[MAX_PATH] = "";
663     char szPath[MAX_PATH] = "";
664     NETRESOURCE nr;
665     DWORD res;
666     DWORD ioctlDebug = IoctlDebug();
667     DWORD gle;
668     DWORD dwAttrib;
669     DWORD dwSize = sizeof(szUser);
670     BOOL  usingRDR = FALSE;
671     int saveerrno;
672     UINT driveType;
673     int sharingViolation;
674
675     memset(HostName, '\0', sizeof(HostName));
676     gethostname(HostName, sizeof(HostName));
677     if (!DisableServiceManagerCheck() &&
678         GetServiceStatus(HostName, TEXT("TransarcAFSDaemon"), &CurrentState) == NOERROR &&
679         CurrentState != SERVICE_RUNNING)
680     {
681         if ( ioctlDebug ) {
682             saveerrno = errno;
683             fprintf(stderr, "pioctl GetServiceStatus(%s) == %d\r\n",
684                     HostName, CurrentState);
685             errno = saveerrno;
686         }
687         return -1;
688     }
689
690     if (RDR_Ready()) {
691         usingRDR = TRUE;
692
693         if ( ioctlDebug ) {
694             saveerrno = errno;
695             fprintf(stderr, "pioctl Redirector is ready\r\n");
696             errno = saveerrno;
697         }
698
699         if (RegOpenKey (HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY, &hk) == 0)
700         {
701             DWORD dwSize = sizeof(netbiosName);
702             DWORD dwType = REG_SZ;
703             RegQueryValueExA (hk, "NetbiosName", NULL, &dwType, (PBYTE)netbiosName, &dwSize);
704             RegCloseKey (hk);
705
706             if ( ioctlDebug ) {
707                 saveerrno = errno;
708                 fprintf(stderr, "pioctl NetbiosName = \"%s\"\r\n", netbiosName);
709                 errno = saveerrno;
710             }
711         } else {
712             if ( ioctlDebug ) {
713                 saveerrno = errno;
714                 gle = GetLastError();
715                 fprintf(stderr, "pioctl Unable to open \"HKLM\\%s\" using NetbiosName = \"AFS\" GLE=0x%x\r\n",
716                         HostName, CurrentState, gle);
717                 errno = saveerrno;
718             }
719         }
720     } else {
721         if ( ioctlDebug ) {
722             saveerrno = errno;
723             fprintf(stderr, "pioctl Redirector is not ready\r\n");
724             errno = saveerrno;
725         }
726
727         if (!GetEnvironmentVariable("AFS_PIOCTL_SERVER", netbiosName, sizeof(netbiosName)))
728             lana_GetNetbiosName(netbiosName,LANA_NETBIOS_NAME_FULL);
729
730         if ( ioctlDebug ) {
731             saveerrno = errno;
732             fprintf(stderr, "pioctl NetbiosName = \"%s\"\r\n", netbiosName);
733             errno = saveerrno;
734         }
735     }
736
737     if (fileNamep) {
738         drivep = strchr(fileNamep, ':');
739         if (drivep && (drivep - fileNamep) >= 1) {
740             tbuffer[0] = *(drivep - 1);
741             tbuffer[1] = ':';
742             tbuffer[2] = '\0';
743
744             driveType = GetDriveType(tbuffer);
745             switch (driveType) {
746             case DRIVE_UNKNOWN:
747             case DRIVE_REMOTE:
748                 if (DriveIsMappedToAFS(tbuffer, netbiosName) ||
749                     DriveIsGlobalAutoMapped(tbuffer))
750                     strcpy(&tbuffer[2], SMB_IOCTL_FILENAME);
751                 else
752                     return -1;
753                 break;
754             default:
755                 if (DriveIsGlobalAutoMapped(tbuffer))
756                     strcpy(&tbuffer[2], SMB_IOCTL_FILENAME);
757                 else
758                     return -1;
759             }
760         } else if (fileNamep[0] == fileNamep[1] &&
761                    (fileNamep[0] == '\\' || fileNamep[0] == '/'))
762         {
763             int count = 0, i = 0;
764
765             while (count < 4 && fileNamep[i]) {
766                 tbuffer[i] = fileNamep[i];
767                 if ( tbuffer[i] == '\\' ||
768                      tbuffer[i] == '/')
769                     count++;
770                 i++;
771             }
772             if (fileNamep[i] == 0 || (fileNamep[i-1] != '\\' && fileNamep[i-1] != '/'))
773                 tbuffer[i++] = '\\';
774             tbuffer[i] = 0;
775             strcat(tbuffer, SMB_IOCTL_FILENAME_NOSLASH);
776         } else {
777             char curdir[MAX_PATH]="";
778
779             GetCurrentDirectory(sizeof(curdir), curdir);
780             if ( curdir[1] == ':' ) {
781                 tbuffer[0] = curdir[0];
782                 tbuffer[1] = ':';
783                 tbuffer[2] = '\0';
784
785                 driveType = GetDriveType(tbuffer);
786                 switch (driveType) {
787                 case DRIVE_UNKNOWN:
788                 case DRIVE_REMOTE:
789                     if (DriveIsMappedToAFS(tbuffer, netbiosName) ||
790                         DriveIsGlobalAutoMapped(tbuffer))
791                         strcpy(&tbuffer[2], SMB_IOCTL_FILENAME);
792                     else
793                         return -1;
794                     break;
795                 default:
796                     if (DriveIsGlobalAutoMapped(tbuffer))
797                         strcpy(&tbuffer[2], SMB_IOCTL_FILENAME);
798                     else
799                         return -1;
800                 }
801             } else if (curdir[0] == curdir[1] &&
802                        (curdir[0] == '\\' || curdir[0] == '/'))
803             {
804                 int count = 0, i = 0;
805
806                 while (count < 4 && curdir[i]) {
807                     tbuffer[i] = curdir[i];
808                     if ( tbuffer[i] == '\\' ||
809                          tbuffer[i] == '/')
810                         count++;
811                     i++;
812                 }
813                 if (curdir[i] == 0 || (curdir[i-1] != '\\' && curdir[i-1] != '/'))
814                     tbuffer[i++] = '\\';
815                 tbuffer[i] = 0;
816                 strcat(tbuffer, SMB_IOCTL_FILENAME_NOSLASH);
817             }
818         }
819     }
820     if (!tbuffer[0]) {
821         /* No file name starting with drive colon specified, use UNC name */
822         sprintf(tbuffer,"\\\\%s\\all%s",netbiosName,SMB_IOCTL_FILENAME);
823     }
824
825     if ( ioctlDebug ) {
826         saveerrno = errno;
827         fprintf(stderr, "pioctl filename = \"%s\"\r\n", tbuffer);
828         errno = saveerrno;
829     }
830
831     fflush(stdout);
832
833     /*
834      * Try to find the correct path and authentication
835      */
836     dwAttrib = GetFileAttributes(tbuffer);
837     if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
838         int  gonext = 0;
839
840         gle = GetLastError();
841         if (gle && ioctlDebug ) {
842             char buf[4096];
843
844             saveerrno = errno;
845             if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
846                                NULL,
847                                gle,
848                                MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
849                                buf,
850                                4096,
851                                (va_list *) NULL
852                                ) )
853             {
854                 fprintf(stderr,"pioctl GetFileAttributes(%s) failed: 0x%X\r\n\t[%s]\r\n",
855                         tbuffer,gle,buf);
856             }
857             errno = saveerrno;
858             SetLastError(gle);
859         }
860
861         /* with the redirector interface, fail immediately.  there is nothing to retry */
862         if (usingRDR)
863             return -1;
864
865         if (!GetEnvironmentVariable("AFS_PIOCTL_SERVER", szClient, sizeof(szClient)))
866             lana_GetNetbiosName(szClient, LANA_NETBIOS_NAME_FULL);
867
868         if (RegOpenKey (HKEY_CURRENT_USER,
869                          TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), &hk) == 0)
870         {
871             DWORD dwType = REG_SZ;
872             RegQueryValueEx (hk, TEXT("Logon User Name"), NULL, &dwType, (PBYTE)szUser, &dwSize);
873             RegCloseKey (hk);
874         }
875
876         if ( szUser[0] ) {
877             if ( ioctlDebug ) {
878                 saveerrno = errno;
879                 fprintf(stderr, "pioctl Explorer logon user: [%s]\r\n",szUser);
880                 errno = saveerrno;
881             }
882             sprintf(szPath, "\\\\%s", szClient);
883             memset (&nr, 0x00, sizeof(NETRESOURCE));
884             nr.dwType=RESOURCETYPE_DISK;
885             nr.lpLocalName=0;
886             nr.lpRemoteName=szPath;
887             res = WNetAddConnection2(&nr,NULL,szUser,0);
888             if (res) {
889                 if ( ioctlDebug ) {
890                     saveerrno = errno;
891                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
892                              szPath,szUser,res);
893                     errno = saveerrno;
894                 }
895                 gonext = 1;
896             }
897
898             sprintf(szPath, "\\\\%s\\all", szClient);
899             res = WNetAddConnection2(&nr,NULL,szUser,0);
900             if (res) {
901                 if ( ioctlDebug ) {
902                     saveerrno = errno;
903                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
904                              szPath,szUser,res);
905                     errno = saveerrno;
906                 }
907                 gonext = 1;
908             }
909
910             if (gonext)
911                 goto try_lsa_principal;
912
913             dwAttrib = GetFileAttributes(tbuffer);
914             if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
915                 gle = GetLastError();
916                 if (gle && ioctlDebug ) {
917                     char buf[4096];
918
919                     saveerrno = errno;
920                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
921                                         NULL,
922                                         gle,
923                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
924                                         buf,
925                                         4096,
926                                         (va_list *) NULL
927                                         ) )
928                     {
929                         fprintf(stderr,"pioctl GetFileAttributes(%s) failed: 0x%X\r\n\t[%s]\r\n",
930                                  tbuffer,gle,buf);
931                     }
932                     errno = saveerrno;
933                     SetLastError(gle);
934                 }
935             }
936         }
937     }
938
939   try_lsa_principal:
940     if (!usingRDR &&
941         dwAttrib == INVALID_FILE_ATTRIBUTES) {
942         int  gonext = 0;
943
944         dwSize = sizeof(szUser);
945         if (GetLSAPrincipalName(szUser, dwSize)) {
946             if ( ioctlDebug ) {
947                 saveerrno = errno;
948                 fprintf(stderr, "pioctl LSA Principal logon user: [%s]\r\n",szUser);
949                 errno = saveerrno;
950             }
951             sprintf(szPath, "\\\\%s", szClient);
952             memset (&nr, 0x00, sizeof(NETRESOURCE));
953             nr.dwType=RESOURCETYPE_DISK;
954             nr.lpLocalName=0;
955             nr.lpRemoteName=szPath;
956             res = WNetAddConnection2(&nr,NULL,szUser,0);
957             if (res) {
958                 if ( ioctlDebug ) {
959                     saveerrno = errno;
960                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
961                              szPath,szUser,res);
962                     errno = saveerrno;
963                 }
964                 gonext = 1;
965             }
966
967             sprintf(szPath, "\\\\%s\\all", szClient);
968             res = WNetAddConnection2(&nr,NULL,szUser,0);
969             if (res) {
970                 if ( ioctlDebug ) {
971                     saveerrno = errno;
972                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
973                              szPath,szUser,res);
974                     errno = saveerrno;
975                 }
976                 gonext = 1;
977             }
978
979             if (gonext)
980                 goto try_sam_compat;
981
982             dwAttrib = GetFileAttributes(tbuffer);
983             if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
984                 gle = GetLastError();
985                 if (gle && ioctlDebug ) {
986                     char buf[4096];
987
988                     saveerrno = errno;
989                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
990                                         NULL,
991                                         gle,
992                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
993                                         buf,
994                                         4096,
995                                         (va_list *) NULL
996                                         ) )
997                     {
998                         fprintf(stderr,"pioctl GetFileAttributes(%s) failed: 0x%X\r\n\t[%s]\r\n",
999                                  tbuffer,gle,buf);
1000                     }
1001                     errno = saveerrno;
1002                     SetLastError(gle);
1003                 }
1004             }
1005         }
1006     }
1007
1008   try_sam_compat:
1009     if (!usingRDR &&
1010         dwAttrib == INVALID_FILE_ATTRIBUTES) {
1011         dwSize = sizeof(szUser);
1012         if (GetUserNameEx(NameSamCompatible, szUser, &dwSize)) {
1013             if ( ioctlDebug ) {
1014                 saveerrno = errno;
1015                 fprintf(stderr, "pioctl SamCompatible logon user: [%s]\r\n",szUser);
1016                 errno = saveerrno;
1017             }
1018             sprintf(szPath, "\\\\%s", szClient);
1019             memset (&nr, 0x00, sizeof(NETRESOURCE));
1020             nr.dwType=RESOURCETYPE_DISK;
1021             nr.lpLocalName=0;
1022             nr.lpRemoteName=szPath;
1023             res = WNetAddConnection2(&nr,NULL,szUser,0);
1024             if (res) {
1025                 if ( ioctlDebug ) {
1026                     saveerrno = errno;
1027                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
1028                              szPath,szUser,res);
1029                     errno = saveerrno;
1030                 }
1031             }
1032
1033             sprintf(szPath, "\\\\%s\\all", szClient);
1034             res = WNetAddConnection2(&nr,NULL,szUser,0);
1035             if (res) {
1036                 if ( ioctlDebug ) {
1037                     saveerrno = errno;
1038                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
1039                              szPath,szUser,res);
1040                     errno = saveerrno;
1041                 }
1042                 return -1;
1043             }
1044
1045             dwAttrib = GetFileAttributes(tbuffer);
1046             if (dwAttrib == INVALID_FILE_ATTRIBUTES) {
1047                 gle = GetLastError();
1048                 if (gle && ioctlDebug ) {
1049                     char buf[4096];
1050
1051                     saveerrno = errno;
1052                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
1053                                         NULL,
1054                                         gle,
1055                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
1056                                         buf,
1057                                         4096,
1058                                         (va_list *) NULL
1059                                         ) )
1060                     {
1061                         fprintf(stderr,"pioctl GetFileAttributes(%s) failed: 0x%X\r\n\t[%s]\r\n",
1062                                  tbuffer,gle,buf);
1063                     }
1064                     errno = saveerrno;
1065                 }
1066                 return -1;
1067             }
1068         } else {
1069             fprintf(stderr, "GetUserNameEx(NameSamCompatible) failed: 0x%X\r\n", GetLastError());
1070             return -1;
1071         }
1072     }
1073
1074     if ( dwAttrib != (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) {
1075         fprintf(stderr, "GetFileAttributes(%s) returned: 0x%08X\r\n",
1076                 tbuffer, dwAttrib);
1077         return -1;
1078     }
1079
1080     /* tbuffer now contains the correct path; now open the file */
1081     sharingViolation = 0;
1082     do {
1083         if (sharingViolation)
1084             Sleep(100);
1085         fh = CreateFile(tbuffer, FILE_READ_DATA | FILE_WRITE_DATA,
1086                         FILE_SHARE_READ, NULL, OPEN_EXISTING,
1087                         FILE_FLAG_WRITE_THROUGH, NULL);
1088         sharingViolation++;
1089     } while (fh == INVALID_HANDLE_VALUE &&
1090              GetLastError() == ERROR_SHARING_VIOLATION &&
1091              sharingViolation < 100);
1092     fflush(stdout);
1093
1094     if (fh == INVALID_HANDLE_VALUE)
1095         return -1;
1096
1097     /* return fh and success code */
1098     *handlep = fh;
1099     return 0;
1100 }
1101
1102 static long
1103 Transceive(HANDLE handle, fs_ioctlRequest_t * reqp)
1104 {
1105     long rcount;
1106     long ioCount;
1107     DWORD gle;
1108     DWORD ioctlDebug = IoctlDebug();
1109     int save;
1110
1111     rcount = (long)(reqp->mp - reqp->data);
1112     if (rcount <= 0) {
1113         if ( ioctlDebug ) {
1114             save = errno;
1115             fprintf(stderr, "pioctl Transceive rcount <= 0: %d\r\n",rcount);
1116             errno = save;
1117         }
1118         return EINVAL;          /* not supposed to happen */
1119     }
1120
1121     if (!WriteFile(handle, reqp->data, rcount, &ioCount, NULL)) {
1122         /* failed to write */
1123         gle = GetLastError();
1124
1125         if ( ioctlDebug ) {
1126             save = errno;
1127             fprintf(stderr, "pioctl Transceive WriteFile failed: 0x%X\r\n",gle);
1128             errno = save;
1129         }
1130         return gle;
1131     }
1132
1133     if (!ReadFile(handle, reqp->data, sizeof(reqp->data), &ioCount, NULL)) {
1134         /* failed to read */
1135         gle = GetLastError();
1136
1137         if ( ioctlDebug ) {
1138             save = errno;
1139             fprintf(stderr, "pioctl Transceive ReadFile failed: 0x%X\r\n",gle);
1140             errno = save;
1141         }
1142         return gle;
1143     }
1144
1145     reqp->nbytes = ioCount;     /* set # of bytes available */
1146     reqp->mp = reqp->data;      /* restart marshalling */
1147
1148     /* return success */
1149     return 0;
1150 }
1151
1152 static long
1153 MarshallLong(fs_ioctlRequest_t * reqp, long val)
1154 {
1155     memcpy(reqp->mp, &val, 4);
1156     reqp->mp += 4;
1157     return 0;
1158 }
1159
1160 static long
1161 UnmarshallLong(fs_ioctlRequest_t * reqp, long *valp)
1162 {
1163     int save;
1164
1165     /* not enough data left */
1166     if (reqp->nbytes < 4) {
1167         if ( IoctlDebug() ) {
1168             save = errno;
1169             fprintf(stderr, "pioctl UnmarshallLong reqp->nbytes < 4: %d\r\n",
1170                      reqp->nbytes);
1171             errno = save;
1172         }
1173         return -1;
1174     }
1175
1176     memcpy(valp, reqp->mp, 4);
1177     reqp->mp += 4;
1178     reqp->nbytes -= 4;
1179     return 0;
1180 }
1181
1182 /* includes marshalling NULL pointer as a null (0 length) string */
1183 static long
1184 MarshallString(fs_ioctlRequest_t * reqp, char *stringp, int is_utf8)
1185 {
1186     int count;
1187     int save;
1188
1189     if (stringp)
1190         count = (int)strlen(stringp) + 1;/* space required including null */
1191     else
1192         count = 1;
1193
1194     if (is_utf8) {
1195         count += utf8_prefix_size;
1196     }
1197
1198     /* watch for buffer overflow */
1199     if ((reqp->mp - reqp->data) + count > sizeof(reqp->data)) {
1200         if ( IoctlDebug() ) {
1201             save = errno;
1202             fprintf(stderr, "pioctl MarshallString buffer overflow\r\n");
1203             errno = save;
1204         }
1205         return -1;
1206     }
1207
1208     if (is_utf8) {
1209         memcpy(reqp->mp, utf8_prefix, utf8_prefix_size);
1210         reqp->mp += utf8_prefix_size;
1211         count -= utf8_prefix_size;
1212     }
1213
1214     if (stringp)
1215         memcpy(reqp->mp, stringp, count);
1216     else
1217         *(reqp->mp) = 0;
1218     reqp->mp += count;
1219     return 0;
1220 }
1221
1222 /* take a path with a drive letter, possibly relative, and return a full path
1223  * without the drive letter.  This is the full path relative to the working
1224  * dir for that drive letter.  The input and output paths can be the same.
1225  */
1226 static long
1227 fs_GetFullPath(char *pathp, char *outPathp, long outSize)
1228 {
1229     char tpath[1000];
1230     char origPath[1000];
1231     char *firstp;
1232     long code;
1233     int pathHasDrive;
1234     int doSwitch;
1235     char newPath[3];
1236     char * p;
1237     int save;
1238
1239     if (pathp[0] != 0 && pathp[1] == ':') {
1240         /* there's a drive letter there */
1241         firstp = pathp + 2;
1242         pathHasDrive = 1;
1243     } else {
1244         firstp = pathp;
1245         pathHasDrive = 0;
1246     }
1247
1248     if ( firstp[0] == '\\' && firstp[1] == '\\' ||
1249          firstp[0] == '/' && firstp[1] == '/') {
1250         /* UNC path - strip off the server and sharename */
1251         int i, count;
1252         for ( i=2,count=2; count < 4 && firstp[i]; i++ ) {
1253             if ( firstp[i] == '\\' || firstp[i] == '/' ) {
1254                 count++;
1255             }
1256         }
1257         if ( firstp[i] == 0 ) {
1258             strcpy(outPathp,"\\");
1259         } else {
1260             strcpy(outPathp,&firstp[--i]);
1261         }
1262         for (p=outPathp ;*p; p++) {
1263             if (*p == '/')
1264                 *p = '\\';
1265         }
1266         return 0;
1267     } else if (firstp[0] == '\\' || firstp[0] == '/') {
1268         /* already an absolute pathname, just copy it back */
1269         strcpy(outPathp, firstp);
1270         for (p=outPathp ;*p; p++) {
1271             if (*p == '/')
1272                 *p = '\\';
1273         }
1274         return 0;
1275     }
1276
1277     GetCurrentDirectory(sizeof(origPath), origPath);
1278
1279     doSwitch = 0;
1280     if (pathHasDrive && (*pathp & ~0x20) != (origPath[0] & ~0x20)) {
1281         /* a drive has been specified and it isn't our current drive.
1282          * to get path, switch to it first.  Must case-fold drive letters
1283          * for user convenience.
1284          */
1285         doSwitch = 1;
1286         newPath[0] = *pathp;
1287         newPath[1] = ':';
1288         newPath[2] = 0;
1289         if (!SetCurrentDirectory(newPath)) {
1290             code = GetLastError();
1291
1292             if ( IoctlDebug() ) {
1293                 save = errno;
1294                 fprintf(stderr, "pioctl fs_GetFullPath SetCurrentDirectory(%s) failed: 0x%X\r\n",
1295                          newPath, code);
1296                 errno = save;
1297             }
1298             return code;
1299         }
1300     }
1301
1302     /* now get the absolute path to the current wdir in this drive */
1303     GetCurrentDirectory(sizeof(tpath), tpath);
1304     if (tpath[1] == ':')
1305         strcpy(outPathp, tpath + 2);    /* skip drive letter */
1306     else if ( tpath[0] == '\\' && tpath[1] == '\\'||
1307               tpath[0] == '/' && tpath[1] == '/') {
1308         /* UNC path - strip off the server and sharename */
1309         int i, count;
1310         for ( i=2,count=2; count < 4 && tpath[i]; i++ ) {
1311             if ( tpath[i] == '\\' || tpath[i] == '/' ) {
1312                 count++;
1313             }
1314         }
1315         if ( tpath[i] == 0 ) {
1316             strcpy(outPathp,"\\");
1317         } else {
1318             strcpy(outPathp,&tpath[--i]);
1319         }
1320     } else {
1321         /* this should never happen */
1322         strcpy(outPathp, tpath);
1323     }
1324
1325     /* if there is a non-null name after the drive, append it */
1326     if (*firstp != 0) {
1327         int len = (int)strlen(outPathp);
1328         if (outPathp[len-1] != '\\' && outPathp[len-1] != '/')
1329             strcat(outPathp, "\\");
1330         strcat(outPathp, firstp);
1331     }
1332
1333     /* finally, if necessary, switch back to our home drive letter */
1334     if (doSwitch) {
1335         SetCurrentDirectory(origPath);
1336     }
1337
1338     for (p=outPathp ;*p; p++) {
1339         if (*p == '/')
1340             *p = '\\';
1341     }
1342     return 0;
1343 }
1344
1345 static int
1346 pioctl_int(char *pathp, afs_int32 opcode, struct ViceIoctl *blobp, afs_int32 follow, afs_int32 is_utf8)
1347 {
1348     fs_ioctlRequest_t preq;
1349     long code;
1350     long temp;
1351     char fullPath[1000];
1352     char altPath[1024];
1353     HANDLE reqHandle;
1354     int save;
1355     int i,j,count,all;
1356
1357     /*
1358      * The pioctl operations for creating a mount point and a symlink are broken.
1359      * Instead of 'pathp' referring to the directory object in which the symlink
1360      * or mount point within which the new object is to be created, 'pathp' refers
1361      * to the object itself.  This results in a problem when the object being created
1362      * is located within the Freelance root.afs volume.  \\afs\foo will not be a
1363      * valid share name since the 'foo' object does not yet exist.  Therefore,
1364      * \\afs\foo\_._.afs_ioctl_._ cannot be opened.  Instead in these two cases
1365      * we must force the use of the \\afs\all\foo form of the path.
1366      *
1367      * We cannot use this form in all cases because of smb submounts which are
1368      * not located within the Freelance local root.
1369      */
1370     switch ( opcode ) {
1371     case VIOC_AFS_CREATE_MT_PT:
1372     case VIOC_SYMLINK:
1373         if (pathp &&
1374              (pathp[0] == '\\' && pathp[1] == '\\' ||
1375               pathp[0] == '/' && pathp[1] == '/')) {
1376             for (all = count = j = 0; pathp[j]; j++) {
1377                 if (pathp[j] == '\\' || pathp[j] == '/')
1378                     count++;
1379
1380                 /* Test to see if the second component is 'all' */
1381                 if (count == 3) {
1382                     all = 1;
1383                     for (i=0; pathp[i+j]; i++) {
1384                         switch(i) {
1385                         case 0:
1386                             if (pathp[i+j] != 'a' &&
1387                                 pathp[i+j] != 'A') {
1388                                 all = 0;
1389                                 goto notall;
1390                             }
1391                             break;
1392                         case 1:
1393                         case 2:
1394                             if (pathp[i+j] != 'l' &&
1395                                  pathp[i+j] != 'L') {
1396                                 all = 0;
1397                                 goto notall;
1398                             }
1399                             break;
1400                         default:
1401                             all = 0;
1402                             goto notall;
1403                         }
1404                     }
1405                     if (i != 3)
1406                         all = 0;
1407                 }
1408
1409               notall:
1410                 if (all)
1411                     break;
1412             }
1413
1414             /*
1415              * if count is three and the second component is not 'all',
1416              * then we are attempting to create an object in the
1417              * Freelance root.afs volume.  Substitute the path.
1418              */
1419
1420             if (count == 3 && !all) {
1421                 /* Normalize the name to use \\afs\all as the root */
1422                 for (count = i = j = 0; pathp[j] && i < sizeof(altPath); j++) {
1423                     if (pathp[j] == '\\' || pathp[j] == '/') {
1424                         altPath[i++] = '\\';
1425                         count++;
1426
1427                         if (count == 3) {
1428                             altPath[i++] = 'a';
1429                             altPath[i++] = 'l';
1430                             altPath[i++] = 'l';
1431                             altPath[i++] = '\\';
1432                             count++;
1433                         }
1434                     } else {
1435                         altPath[i++] = pathp[j];
1436                     }
1437                 }
1438                 altPath[i] = '\0';
1439                 pathp = altPath;
1440             }
1441         }
1442     }
1443
1444     code = GetIoctlHandle(pathp, &reqHandle);
1445     if (code) {
1446         if (pathp)
1447             errno = EINVAL;
1448         else
1449             errno = ENODEV;
1450         return code;
1451     }
1452
1453     /* init the request structure */
1454     InitFSRequest(&preq);
1455
1456     /* marshall the opcode, the path name and the input parameters */
1457     MarshallLong(&preq, opcode);
1458     /* when marshalling the path, remove the drive letter, since we already
1459      * used the drive letter to find the AFS daemon; we don't need it any more.
1460      * Eventually we'll expand relative path names here, too, since again, only
1461      * we understand those.
1462      */
1463     if (pathp) {
1464         code = fs_GetFullPath(pathp, fullPath, sizeof(fullPath));
1465         if (code) {
1466             CloseHandle(reqHandle);
1467             errno = EINVAL;
1468             return code;
1469         }
1470     } else {
1471         strcpy(fullPath, "");
1472     }
1473
1474     MarshallString(&preq, fullPath, is_utf8);
1475     if (blobp->in_size) {
1476         if (blobp->in_size > sizeof(preq.data) - (preq.mp - preq.data)*sizeof(char)) {
1477             errno = E2BIG;
1478             return -1;
1479         }
1480         memcpy(preq.mp, blobp->in, blobp->in_size);
1481         preq.mp += blobp->in_size;
1482     }
1483
1484     /* now make the call */
1485     code = Transceive(reqHandle, &preq);
1486     if (code) {
1487         CloseHandle(reqHandle);
1488         return code;
1489     }
1490
1491     /* now unmarshall the return value */
1492     if (UnmarshallLong(&preq, &temp) != 0) {
1493         CloseHandle(reqHandle);
1494         return -1;
1495     }
1496
1497     if (temp != 0) {
1498         CloseHandle(reqHandle);
1499         errno = CMtoUNIXerror(temp);
1500         if ( IoctlDebug() ) {
1501             save = errno;
1502             fprintf(stderr, "pioctl temp != 0: 0x%X\r\n",temp);
1503             errno = save;
1504         }
1505         return -1;
1506     }
1507
1508     /* otherwise, unmarshall the output parameters */
1509     if (blobp->out_size) {
1510         temp = blobp->out_size;
1511         if (preq.nbytes < temp)
1512             temp = preq.nbytes;
1513         memcpy(blobp->out, preq.mp, temp);
1514         blobp->out_size = temp;
1515     }
1516
1517     /* and return success */
1518     CloseHandle(reqHandle);
1519     return 0;
1520 }
1521
1522 int
1523 pioctl_utf8(char * pathp, afs_int32 opcode, struct ViceIoctl * blobp, afs_int32 follow)
1524 {
1525     return pioctl_int(pathp, opcode, blobp, follow, TRUE);
1526 }
1527
1528 int
1529 pioctl(char * pathp, afs_int32 opcode, struct ViceIoctl * blobp, afs_int32 follow)
1530 {
1531     return pioctl_int(pathp, opcode, blobp, follow, FALSE);
1532 }
1533