Make Windows pioctl prototypes consistent with Unix
[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
13
14 #include <afs/stds.h>
15 #include <windows.h>
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <errno.h>
19 #include <malloc.h>
20 #include <string.h>
21 #include <winioctl.h>
22 #include <winsock2.h>
23 #define SECURITY_WIN32
24 #include <security.h>
25 #include <nb30.h>
26
27 #include <osi.h>
28
29 #include <cm.h>
30 #include <cm_nls.h>
31 #include <cm_server.h>
32 #include <cm_cell.h>
33 #include <cm_user.h>
34 #include <cm_conn.h>
35 #include <cm_scache.h>
36 #include <cm_buf.h>
37 #include <cm_dir.h>
38 #include <cm_utils.h>
39 #include <cm_ioctl.h>
40
41 #include <smb.h>
42 #include <pioctl_nt.h>
43 #include <WINNT/afsreg.h>
44 #include <lanahelper.h>
45
46 #include <loadfuncs-krb5.h>
47 #include <krb5.h>
48
49 static char AFSConfigKeyName[] = AFSREG_CLT_SVC_PARAM_SUBKEY;
50
51 static const char utf8_prefix[] = UTF8_PREFIX;
52 static const int  utf8_prefix_size = sizeof(utf8_prefix) -  sizeof(char);
53
54 #define FS_IOCTLREQUEST_MAXSIZE 8192
55 /* big structure for representing and storing an IOCTL request */
56 typedef struct fs_ioctlRequest {
57     char *mp;                   /* marshalling/unmarshalling ptr */
58     long nbytes;                /* bytes received (when unmarshalling) */
59     char data[FS_IOCTLREQUEST_MAXSIZE]; /* data we're marshalling */
60 } fs_ioctlRequest_t;
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 DisableServiceManagerCheck(void)
146 {
147     static int init = 0;
148     static BOOL smcheck = 0;
149
150     if ( !init ) {
151         HKEY hk;
152
153         if (RegOpenKey (HKEY_LOCAL_MACHINE, 
154                          TEXT("Software\\OpenAFS\\Client"), &hk) == 0)
155         {
156             DWORD dwSize = sizeof(BOOL);
157             DWORD dwType = REG_DWORD;
158             RegQueryValueEx (hk, TEXT("DisableIoctlSMCheck"), NULL, &dwType, (PBYTE)&smcheck, &dwSize);
159             RegCloseKey (hk);
160         }
161
162         init = 1;
163     }
164
165     return smcheck;
166 }
167
168 static DWORD 
169 GetServiceStatus(
170     LPSTR lpszMachineName, 
171     LPSTR lpszServiceName,
172     DWORD *lpdwCurrentState) 
173
174     DWORD           hr               = NOERROR; 
175     SC_HANDLE       schSCManager     = NULL; 
176     SC_HANDLE       schService       = NULL; 
177     DWORD           fdwDesiredAccess = 0; 
178     SERVICE_STATUS  ssServiceStatus  = {0}; 
179     BOOL            fRet             = FALSE; 
180
181     *lpdwCurrentState = 0; 
182  
183     fdwDesiredAccess = GENERIC_READ; 
184  
185     schSCManager = OpenSCManager(lpszMachineName,  
186                                  NULL,
187                                  fdwDesiredAccess); 
188  
189     if(schSCManager == NULL) 
190     { 
191         hr = GetLastError();
192         goto cleanup; 
193     } 
194  
195     schService = OpenService(schSCManager,
196                              lpszServiceName,
197                              fdwDesiredAccess); 
198  
199     if(schService == NULL) 
200     { 
201         hr = GetLastError();
202         goto cleanup; 
203     } 
204  
205     fRet = QueryServiceStatus(schService,
206                               &ssServiceStatus); 
207  
208     if(fRet == FALSE) 
209     { 
210         hr = GetLastError(); 
211         goto cleanup; 
212     } 
213  
214     *lpdwCurrentState = ssServiceStatus.dwCurrentState; 
215  
216 cleanup: 
217  
218     CloseServiceHandle(schService); 
219     CloseServiceHandle(schSCManager); 
220  
221     return(hr); 
222
223
224 // krb5 functions
225 DECL_FUNC_PTR(krb5_cc_default_name);
226 DECL_FUNC_PTR(krb5_cc_set_default_name);
227 DECL_FUNC_PTR(krb5_get_default_config_files);
228 DECL_FUNC_PTR(krb5_free_config_files);
229 DECL_FUNC_PTR(krb5_free_context);
230 DECL_FUNC_PTR(krb5_get_default_realm);
231 DECL_FUNC_PTR(krb5_free_default_realm);
232 DECL_FUNC_PTR(krb5_init_context);
233 DECL_FUNC_PTR(krb5_cc_default);
234 DECL_FUNC_PTR(krb5_parse_name);
235 DECL_FUNC_PTR(krb5_free_principal);
236 DECL_FUNC_PTR(krb5_cc_close);
237 DECL_FUNC_PTR(krb5_cc_get_principal);
238 DECL_FUNC_PTR(krb5_build_principal);
239 DECL_FUNC_PTR(krb5_c_random_make_octets);
240 DECL_FUNC_PTR(krb5_get_init_creds_password);
241 DECL_FUNC_PTR(krb5_free_cred_contents);
242 DECL_FUNC_PTR(krb5_cc_resolve);
243 DECL_FUNC_PTR(krb5_unparse_name);
244 DECL_FUNC_PTR(krb5_free_unparsed_name);
245
246 FUNC_INFO krb5_fi[] = {
247     MAKE_FUNC_INFO(krb5_cc_default_name),
248     MAKE_FUNC_INFO(krb5_cc_set_default_name),
249     MAKE_FUNC_INFO(krb5_get_default_config_files),
250     MAKE_FUNC_INFO(krb5_free_config_files),
251     MAKE_FUNC_INFO(krb5_free_context),
252     MAKE_FUNC_INFO(krb5_get_default_realm),
253     MAKE_FUNC_INFO(krb5_free_default_realm),
254     MAKE_FUNC_INFO(krb5_init_context),
255     MAKE_FUNC_INFO(krb5_cc_default),
256     MAKE_FUNC_INFO(krb5_parse_name),
257     MAKE_FUNC_INFO(krb5_free_principal),
258     MAKE_FUNC_INFO(krb5_cc_close),
259     MAKE_FUNC_INFO(krb5_cc_get_principal),
260     MAKE_FUNC_INFO(krb5_build_principal),
261     MAKE_FUNC_INFO(krb5_c_random_make_octets),
262     MAKE_FUNC_INFO(krb5_get_init_creds_password),
263     MAKE_FUNC_INFO(krb5_free_cred_contents),
264     MAKE_FUNC_INFO(krb5_cc_resolve),
265     MAKE_FUNC_INFO(krb5_unparse_name),
266     MAKE_FUNC_INFO(krb5_free_unparsed_name),
267     END_FUNC_INFO
268 };
269
270 static int
271 LoadFuncs(
272     const char* dll_name,
273     FUNC_INFO fi[],
274     HINSTANCE* ph,  // [out, optional] - DLL handle
275     int* pindex,    // [out, optional] - index of last func loaded (-1 if none)
276     int cleanup,    // cleanup function pointers and unload on error
277     int go_on,      // continue loading even if some functions cannot be loaded
278     int silent      // do not pop-up a system dialog if DLL cannot be loaded
279     )
280 {
281     HINSTANCE h;
282     int i, n, last_i;
283     int error = 0;
284     UINT em;
285
286     if (ph) *ph = 0;
287     if (pindex) *pindex = -1;
288
289     for (n = 0; fi[n].func_ptr_var; n++)
290         *(fi[n].func_ptr_var) = 0;
291
292     if (silent)
293         em = SetErrorMode(SEM_FAILCRITICALERRORS);
294     h = LoadLibrary(dll_name);
295     if (silent)
296         SetErrorMode(em);
297
298     if (!h)
299         return 0;
300
301     last_i = -1;
302     for (i = 0; (go_on || !error) && (i < n); i++)
303     {
304         void* p = (void*)GetProcAddress(h, fi[i].func_name);
305         if (!p)
306             error = 1;
307         else
308         {
309             last_i = i;
310             *(fi[i].func_ptr_var) = p;
311         }
312     }
313     if (pindex) *pindex = last_i;
314     if (error && cleanup && !go_on) {
315         for (i = 0; i < n; i++) {
316             *(fi[i].func_ptr_var) = 0;
317         }
318         FreeLibrary(h);
319         return 0;
320     }
321     if (ph) *ph = h;
322     if (error) return 0;
323     return 1;
324 }
325 #if defined(_IA64_) || defined(_AMD64_)
326 #define KERB5DLL "krb5_64.dll"
327 #else
328 #define KERB5DLL "krb5_32.dll"
329 #endif
330 static BOOL
331 IsKrb5Available()
332 {
333     static HINSTANCE hKrb5DLL = 0;
334
335     if ( hKrb5DLL )
336         return TRUE;
337
338     hKrb5DLL = LoadLibrary(KERB5DLL);
339     if (hKrb5DLL) {
340         if (!LoadFuncs(KERB5DLL, krb5_fi, 0, 0, 1, 0, 0))
341         {
342             FreeLibrary(hKrb5DLL);
343             hKrb5DLL = 0;
344             return FALSE;
345         }
346         return TRUE;
347     }
348     return FALSE;
349 }
350
351 static BOOL
352 GetLSAPrincipalName(char * szUser, DWORD *dwSize)
353 {
354     krb5_context   ctx = 0;
355     krb5_error_code code;
356     krb5_ccache mslsa_ccache=0;
357     krb5_principal princ = 0;
358     char * pname = 0;
359     BOOL success = 0;
360
361     if (!IsKrb5Available())
362         return FALSE;
363
364     if (code = pkrb5_init_context(&ctx))
365         goto cleanup;
366
367     if (code = pkrb5_cc_resolve(ctx, "MSLSA:", &mslsa_ccache))
368         goto cleanup;
369
370     if (code = pkrb5_cc_get_principal(ctx, mslsa_ccache, &princ))
371         goto cleanup;
372
373     if (code = pkrb5_unparse_name(ctx, princ, &pname))
374         goto cleanup;
375
376     if ( strlen(pname) < *dwSize ) {
377         strncpy(szUser, pname, *dwSize);
378         szUser[*dwSize-1] = '\0';
379         success = 1;
380     }
381     *dwSize = (DWORD)strlen(pname);
382
383   cleanup:
384     if (pname)
385         pkrb5_free_unparsed_name(ctx, pname);
386
387     if (princ)
388         pkrb5_free_principal(ctx, princ);
389
390     if (mslsa_ccache)
391         pkrb5_cc_close(ctx, mslsa_ccache);
392
393     if (ctx)
394         pkrb5_free_context(ctx);
395     return success;
396 }
397
398 //
399 // Recursively evaluate drivestr to find the final
400 // dos drive letter to which the source is mapped.
401 //
402 static BOOL
403 DriveSubstitution(char *drivestr, char *subststr, size_t substlen)
404 {
405     char device[MAX_PATH];
406
407     if ( QueryDosDevice(drivestr, device, MAX_PATH) )
408     {
409         if ( device[0] == '\\' &&
410              device[1] == '?' &&
411              device[2] == '?' &&
412              device[3] == '\\' &&
413              isalpha(device[4]) &&
414              device[5] == ':')
415         {
416             device[0] = device[4];
417             device[1] = ':';
418             device[2] = '\0';
419             if ( DriveSubstitution(device, subststr, substlen) )
420             {
421                 return TRUE;
422             } else {
423                 subststr[0] = device[0];
424                 subststr[1] = ':';
425                 subststr[2] = '\0';
426                 return TRUE;
427             }
428         } else 
429         if ( device[0] == '\\' &&
430              device[1] == '?' &&
431              device[2] == '?' &&
432              device[3] == '\\' &&
433              device[4] == 'U' &&
434              device[5] == 'N' &&
435              device[6] == 'C' &&
436              device[7] == '\\')
437         {
438              subststr[0] = '\\';
439              strncpy(&subststr[1], &device[7], substlen-1);
440              subststr[substlen-1] = '\0';
441              return TRUE;
442         }
443     }
444
445     return FALSE;
446 }
447
448 // 
449 // drivestr - is "<drive-letter>:"
450 //
451 static BOOL
452 DriveIsMappedToAFS(char *drivestr, char *NetbiosName)
453 {
454     DWORD dwResult, dwResultEnum;
455     HANDLE hEnum;
456     DWORD cbBuffer = 16384;     // 16K is a good size
457     DWORD cEntries = -1;        // enumerate all possible entries
458     LPNETRESOURCE lpnrLocal;    // pointer to enumerated structures
459     DWORD i;
460     BOOL  bIsAFS = FALSE;
461     char  subststr[MAX_PATH];
462
463     //
464     // Handle drive letter substitution created with "SUBST <drive> <path>".
465     // If a substitution has occurred, use the target drive letter instead
466     // of the source.
467     //
468     if ( DriveSubstitution(drivestr, subststr, MAX_PATH) )
469     {
470         if (subststr[0] == '\\' &&
471             subststr[1] == '\\') 
472         {
473             if (_strnicmp( &subststr[2], NetbiosName, strlen(NetbiosName)) == 0)
474                 return TRUE;
475             else
476                 return FALSE;
477         }
478         drivestr = subststr;
479     }
480
481     //
482     // Call the WNetOpenEnum function to begin the enumeration.
483     //
484     dwResult = WNetOpenEnum(RESOURCE_CONNECTED,
485                             RESOURCETYPE_DISK,
486                             RESOURCEUSAGE_ALL,
487                             NULL,       // NULL first time the function is called
488                             &hEnum);    // handle to the resource
489
490     if (dwResult != NO_ERROR)
491         return FALSE;
492
493     //
494     // Call the GlobalAlloc function to allocate resources.
495     //
496     lpnrLocal = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer);
497     if (lpnrLocal == NULL)
498         return FALSE;
499
500     do {
501         //
502         // Initialize the buffer.
503         //
504         ZeroMemory(lpnrLocal, cbBuffer);
505         //
506         // Call the WNetEnumResource function to continue
507         //  the enumeration.
508         //
509         cEntries = -1;
510         dwResultEnum = WNetEnumResource(hEnum,          // resource handle
511                                         &cEntries,      // defined locally as -1
512                                         lpnrLocal,      // LPNETRESOURCE
513                                         &cbBuffer);     // buffer size
514         //
515         // If the call succeeds, loop through the structures.
516         //
517         if (dwResultEnum == NO_ERROR) {
518             for (i = 0; i < cEntries; i++) {
519                 if (lpnrLocal[i].lpLocalName &&
520                     toupper(lpnrLocal[i].lpLocalName[0]) == toupper(drivestr[0])) {
521                     //
522                     // Skip the two backslashes at the start of the UNC device name
523                     //
524                     if ( _strnicmp( &(lpnrLocal[i].lpRemoteName[2]), NetbiosName, strlen(NetbiosName)) == 0 )
525                     {
526                         bIsAFS = TRUE;
527                         break;
528                     }
529                 }
530             }
531         }
532         // Process errors.
533         //
534         else if (dwResultEnum != ERROR_NO_MORE_ITEMS)
535             break;
536     }
537     while (dwResultEnum != ERROR_NO_MORE_ITEMS);
538     
539     //
540     // Call the GlobalFree function to free the memory.
541     //
542     GlobalFree((HGLOBAL) lpnrLocal);
543     //
544     // Call WNetCloseEnum to end the enumeration.
545     //
546     dwResult = WNetCloseEnum(hEnum);
547
548     return bIsAFS;
549 }
550
551 static BOOL
552 DriveIsGlobalAutoMapped(char *drivestr)
553 {
554     DWORD dwResult;
555     HKEY hKey;
556     DWORD dwSubMountSize;
557     char szSubMount[260];
558     DWORD dwType;
559
560     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
561                             AFSREG_CLT_SVC_PARAM_SUBKEY "\\GlobalAutoMapper", 
562                             0, KEY_QUERY_VALUE, &hKey);
563     if (dwResult != ERROR_SUCCESS)
564         return FALSE;
565
566     dwSubMountSize = sizeof(szSubMount);
567     dwType = REG_SZ;
568     dwResult = RegQueryValueEx(hKey, drivestr, 0, &dwType, szSubMount, &dwSubMountSize);
569     RegCloseKey(hKey);
570
571     if (dwResult == ERROR_SUCCESS && dwType == REG_SZ)
572         return TRUE;
573     else
574         return FALSE;
575 }
576
577 static long
578 GetIoctlHandle(char *fileNamep, HANDLE * handlep)
579 {
580     char *drivep = NULL;
581     char netbiosName[MAX_NB_NAME_LENGTH];
582     DWORD CurrentState = 0;
583     char  HostName[64] = "";
584     char tbuffer[MAX_PATH]="";
585     HANDLE fh;
586     HKEY hk;
587     char szUser[128] = "";
588     char szClient[MAX_PATH] = "";
589     char szPath[MAX_PATH] = "";
590     NETRESOURCE nr;
591     DWORD res;
592     DWORD ioctlDebug = IoctlDebug();
593     DWORD gle;
594     DWORD dwSize = sizeof(szUser);
595     int saveerrno;
596     UINT driveType;
597
598     memset(HostName, '\0', sizeof(HostName));
599     gethostname(HostName, sizeof(HostName));
600     if (!DisableServiceManagerCheck() &&
601         GetServiceStatus(HostName, TEXT("TransarcAFSDaemon"), &CurrentState) == NOERROR &&
602         CurrentState != SERVICE_RUNNING)
603         return -1;
604
605     // Populate the Netbios Name
606     lana_GetNetbiosName(netbiosName,LANA_NETBIOS_NAME_FULL);
607
608     if (fileNamep) {
609         drivep = strchr(fileNamep, ':');
610         if (drivep && (drivep - fileNamep) >= 1) {
611             tbuffer[0] = *(drivep - 1);
612             tbuffer[1] = ':';
613             tbuffer[2] = '\0';
614
615             driveType = GetDriveType(tbuffer);
616             switch (driveType) {
617             case DRIVE_UNKNOWN:
618             case DRIVE_REMOTE:
619                 if (DriveIsMappedToAFS(tbuffer, netbiosName) ||
620                     DriveIsGlobalAutoMapped(tbuffer))
621                     strcpy(&tbuffer[2], SMB_IOCTL_FILENAME);
622                 else 
623                     return -1;
624                 break;
625             default:
626                 if (DriveIsGlobalAutoMapped(tbuffer))
627                     strcpy(&tbuffer[2], SMB_IOCTL_FILENAME);
628                 else 
629                     return -1;
630             }
631         } else if (fileNamep[0] == fileNamep[1] && 
632                    (fileNamep[0] == '\\' || fileNamep[0] == '/'))
633         {
634             int count = 0, i = 0;
635
636             while (count < 4 && fileNamep[i]) {
637                 tbuffer[i] = fileNamep[i];
638                 if ( tbuffer[i] == '\\' ||
639                      tbuffer[i] == '/')
640                     count++;
641                 i++;
642             }
643             if (fileNamep[i] == 0)
644                 tbuffer[i++] = '\\';
645             tbuffer[i] = 0;
646             strcat(tbuffer, SMB_IOCTL_FILENAME);
647         } else {
648             char curdir[MAX_PATH]="";
649
650             GetCurrentDirectory(sizeof(curdir), curdir);
651             if ( curdir[1] == ':' ) {
652                 tbuffer[0] = curdir[0];
653                 tbuffer[1] = ':';
654                 tbuffer[2] = '\0';
655
656                 driveType = GetDriveType(tbuffer);
657                 switch (driveType) {
658                 case DRIVE_UNKNOWN:
659                 case DRIVE_REMOTE:
660                     if (DriveIsMappedToAFS(tbuffer, netbiosName) ||
661                         DriveIsGlobalAutoMapped(tbuffer))
662                         strcpy(&tbuffer[2], SMB_IOCTL_FILENAME);
663                     else 
664                         return -1;
665                     break;
666                 default:
667                     if (DriveIsGlobalAutoMapped(tbuffer))
668                         strcpy(&tbuffer[2], SMB_IOCTL_FILENAME);
669                     else 
670                         return -1;
671                 }
672             } else if (curdir[0] == curdir[1] &&
673                        (curdir[0] == '\\' || curdir[0] == '/')) 
674             {
675                 int count = 0, i = 0;
676
677                 while (count < 4 && curdir[i]) {
678                     tbuffer[i] = curdir[i];
679                     if ( tbuffer[i] == '\\' ||
680                          tbuffer[i] == '/')
681                         count++;
682                     i++;
683                 }
684                 if (tbuffer[i] == 0)
685                     tbuffer[i++] = '\\';
686                 tbuffer[i] = 0;
687                 strcat(tbuffer, SMB_IOCTL_FILENAME_NOSLASH);
688             }
689         }
690     }
691     if (!tbuffer[0]) {
692         /* No file name starting with drive colon specified, use UNC name */
693         sprintf(tbuffer,"\\\\%s\\all%s",netbiosName,SMB_IOCTL_FILENAME);
694     }
695
696     fflush(stdout);
697     /* now open the file */
698     fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
699                     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
700                     FILE_FLAG_WRITE_THROUGH, NULL);
701
702         fflush(stdout);
703
704     if (fh == INVALID_HANDLE_VALUE) {
705         int  gonext = 0;
706
707         gle = GetLastError();
708         if (gle && ioctlDebug ) {
709             char buf[4096];
710             
711             saveerrno = errno;
712             if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
713                                NULL,
714                                gle,
715                                MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
716                                buf,
717                                4096,
718                                (va_list *) NULL
719                                ) )
720             {
721                 fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
722                         tbuffer,gle,buf);
723             }
724             errno = saveerrno;
725         }
726
727         lana_GetNetbiosName(szClient, LANA_NETBIOS_NAME_FULL);
728
729         if (RegOpenKey (HKEY_CURRENT_USER, 
730                          TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), &hk) == 0)
731         {
732             DWORD dwType = REG_SZ;
733             RegQueryValueEx (hk, TEXT("Logon User Name"), NULL, &dwType, (PBYTE)szUser, &dwSize);
734             RegCloseKey (hk);
735         }
736
737         if ( szUser[0] ) {
738             if ( ioctlDebug ) {
739                 saveerrno = errno;
740                 fprintf(stderr, "pioctl Explorer logon user: [%s]\r\n",szUser);
741                 errno = saveerrno;
742             }
743             sprintf(szPath, "\\\\%s", szClient);
744             memset (&nr, 0x00, sizeof(NETRESOURCE));
745             nr.dwType=RESOURCETYPE_DISK;
746             nr.lpLocalName=0;
747             nr.lpRemoteName=szPath;
748             res = WNetAddConnection2(&nr,NULL,szUser,0);
749             if (res) {
750                 if ( ioctlDebug ) {
751                     saveerrno = errno;
752                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
753                              szPath,szUser,res);
754                     errno = saveerrno;
755                 }
756                 gonext = 1;
757             }
758
759             sprintf(szPath, "\\\\%s\\all", szClient);
760             res = WNetAddConnection2(&nr,NULL,szUser,0);
761             if (res) {
762                 if ( ioctlDebug ) {
763                     saveerrno = errno;
764                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
765                              szPath,szUser,res);
766                     errno = saveerrno;
767                 }
768                 gonext = 1;
769             }
770
771             if (gonext)
772                 goto try_lsa_principal;
773
774             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
775                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
776                              FILE_FLAG_WRITE_THROUGH, NULL);
777             fflush(stdout);
778             if (fh == INVALID_HANDLE_VALUE) {
779                 gle = GetLastError();
780                 if (gle && ioctlDebug ) {
781                     char buf[4096];
782
783                     saveerrno = errno;
784                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
785                                         NULL,
786                                         gle,
787                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
788                                         buf,
789                                         4096,
790                                         (va_list *) NULL
791                                         ) )
792                     {
793                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
794                                  tbuffer,gle,buf);
795                     }
796                     errno = saveerrno;
797                 }
798             }
799         }
800     }
801
802   try_lsa_principal:
803     if (fh == INVALID_HANDLE_VALUE) {
804         int  gonext = 0;
805
806         dwSize = sizeof(szUser);
807         if (GetLSAPrincipalName(szUser, &dwSize)) {
808             if ( ioctlDebug ) {
809                 saveerrno = errno;
810                 fprintf(stderr, "pioctl LSA Principal logon user: [%s]\r\n",szUser);
811                 errno = saveerrno;
812             }
813             sprintf(szPath, "\\\\%s", szClient);
814             memset (&nr, 0x00, sizeof(NETRESOURCE));
815             nr.dwType=RESOURCETYPE_DISK;
816             nr.lpLocalName=0;
817             nr.lpRemoteName=szPath;
818             res = WNetAddConnection2(&nr,NULL,szUser,0);
819             if (res) {
820                 if ( ioctlDebug ) {
821                     saveerrno = errno;
822                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
823                              szPath,szUser,res);
824                     errno = saveerrno;
825                 }
826                 gonext = 1;
827             }
828
829             sprintf(szPath, "\\\\%s\\all", szClient);
830             res = WNetAddConnection2(&nr,NULL,szUser,0);
831             if (res) {
832                 if ( ioctlDebug ) {
833                     saveerrno = errno;
834                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
835                              szPath,szUser,res);
836                     errno = saveerrno;
837                 }
838                 gonext = 1;
839             }
840
841             if (gonext)
842                 goto try_sam_compat;
843
844             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
845                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
846                              FILE_FLAG_WRITE_THROUGH, NULL);
847             fflush(stdout);
848             if (fh == INVALID_HANDLE_VALUE) {
849                 gle = GetLastError();
850                 if (gle && ioctlDebug ) {
851                     char buf[4096];
852
853                     saveerrno = errno;
854                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
855                                         NULL,
856                                         gle,
857                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
858                                         buf,
859                                         4096,
860                                         (va_list *) NULL
861                                         ) )
862                     {
863                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
864                                  tbuffer,gle,buf);
865                     }
866                     errno = saveerrno;
867
868                 }
869             }
870         }
871     }
872
873   try_sam_compat:
874     if ( fh == INVALID_HANDLE_VALUE ) {
875         dwSize = sizeof(szUser);
876         if (GetUserNameEx(NameSamCompatible, szUser, &dwSize)) {
877             if ( ioctlDebug ) {
878                 saveerrno = errno;
879                 fprintf(stderr, "pioctl SamCompatible 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             }
896
897             sprintf(szPath, "\\\\%s\\all", szClient);
898             res = WNetAddConnection2(&nr,NULL,szUser,0);
899             if (res) {
900                 if ( ioctlDebug ) {
901                     saveerrno = errno;
902                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
903                              szPath,szUser,res);
904                     errno = saveerrno;
905                 }
906                 return -1;
907             }
908
909             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
910                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
911                              FILE_FLAG_WRITE_THROUGH, NULL);
912             fflush(stdout);
913             if (fh == INVALID_HANDLE_VALUE) {
914                 gle = GetLastError();
915                 if (gle && ioctlDebug ) {
916                     char buf[4096];
917
918                     saveerrno = errno;
919                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
920                                         NULL,
921                                         gle,
922                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
923                                         buf,
924                                         4096,
925                                         (va_list *) NULL
926                                         ) )
927                     {
928                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
929                                  tbuffer,gle,buf);
930                     }
931                     errno = saveerrno;
932                 }
933                 return -1;
934             }
935         } else {
936             fprintf(stderr, "GetUserNameEx(NameSamCompatible) failed: 0x%X\r\n", GetLastError());
937             return -1;
938         }
939     }
940
941     /* return fh and success code */
942     *handlep = fh;
943     return 0;
944 }
945
946 static long
947 Transceive(HANDLE handle, fs_ioctlRequest_t * reqp)
948 {
949     long rcount;
950     long ioCount;
951     DWORD gle;
952     DWORD ioctlDebug = IoctlDebug();
953     int save;
954
955     rcount = (long)(reqp->mp - reqp->data);
956     if (rcount <= 0) {
957         if ( ioctlDebug ) {
958             save = errno;
959             fprintf(stderr, "pioctl Transceive rcount <= 0: %d\r\n",rcount);
960             errno = save;
961         }
962         return EINVAL;          /* not supposed to happen */
963     }
964
965     if (!WriteFile(handle, reqp->data, rcount, &ioCount, NULL)) {
966         /* failed to write */
967         gle = GetLastError();
968
969         if ( ioctlDebug ) {
970             save = errno;
971             fprintf(stderr, "pioctl Transceive WriteFile failed: 0x%X\r\n",gle);
972             errno = save;
973         }
974         return gle;
975     }
976
977     if (!ReadFile(handle, reqp->data, sizeof(reqp->data), &ioCount, NULL)) {
978         /* failed to read */
979         gle = GetLastError();
980
981         if ( ioctlDebug ) {
982             save = errno;
983             fprintf(stderr, "pioctl Transceive ReadFile failed: 0x%X\r\n",gle);
984             errno = save;
985         }
986         return gle;
987     }
988
989     reqp->nbytes = ioCount;     /* set # of bytes available */
990     reqp->mp = reqp->data;      /* restart marshalling */
991
992     /* return success */
993     return 0;
994 }
995
996 static long
997 MarshallLong(fs_ioctlRequest_t * reqp, long val)
998 {
999     memcpy(reqp->mp, &val, 4);
1000     reqp->mp += 4;
1001     return 0;
1002 }
1003
1004 static long
1005 UnmarshallLong(fs_ioctlRequest_t * reqp, long *valp)
1006 {
1007     int save;
1008
1009     /* not enough data left */
1010     if (reqp->nbytes < 4) {
1011         if ( IoctlDebug() ) {
1012             save = errno;
1013             fprintf(stderr, "pioctl UnmarshallLong reqp->nbytes < 4: %d\r\n",
1014                      reqp->nbytes);
1015             errno = save;
1016         }
1017         return -1;
1018     }
1019
1020     memcpy(valp, reqp->mp, 4);
1021     reqp->mp += 4;
1022     reqp->nbytes -= 4;
1023     return 0;
1024 }
1025
1026 /* includes marshalling NULL pointer as a null (0 length) string */
1027 static long
1028 MarshallString(fs_ioctlRequest_t * reqp, char *stringp, int is_utf8)
1029 {
1030     int count;
1031     int save;
1032
1033     if (stringp)
1034         count = (int)strlen(stringp) + 1;/* space required including null */
1035     else
1036         count = 1;
1037
1038     if (is_utf8) {
1039         count += utf8_prefix_size;
1040     }
1041
1042     /* watch for buffer overflow */
1043     if ((reqp->mp - reqp->data) + count > sizeof(reqp->data)) {
1044         if ( IoctlDebug() ) {
1045             save = errno;
1046             fprintf(stderr, "pioctl MarshallString buffer overflow\r\n");
1047             errno = save;
1048         }
1049         return -1;
1050     }
1051
1052     if (is_utf8) {
1053         memcpy(reqp->mp, utf8_prefix, utf8_prefix_size);
1054         reqp->mp += utf8_prefix_size;
1055         count -= utf8_prefix_size;
1056     }
1057
1058     if (stringp)
1059         memcpy(reqp->mp, stringp, count);
1060     else
1061         *(reqp->mp) = 0;
1062     reqp->mp += count;
1063     return 0;
1064 }
1065
1066 /* take a path with a drive letter, possibly relative, and return a full path
1067  * without the drive letter.  This is the full path relative to the working
1068  * dir for that drive letter.  The input and output paths can be the same.
1069  */
1070 static long
1071 fs_GetFullPath(char *pathp, char *outPathp, long outSize)
1072 {
1073     char tpath[1000];
1074     char origPath[1000];
1075     char *firstp;
1076     long code;
1077     int pathHasDrive;
1078     int doSwitch;
1079     char newPath[3];
1080     char * p;
1081     int save;
1082
1083     if (pathp[0] != 0 && pathp[1] == ':') {
1084         /* there's a drive letter there */
1085         firstp = pathp + 2;
1086         pathHasDrive = 1;
1087     } else {
1088         firstp = pathp;
1089         pathHasDrive = 0;
1090     }
1091
1092     if ( firstp[0] == '\\' && firstp[1] == '\\' || 
1093          firstp[0] == '/' && firstp[1] == '/') {
1094         /* UNC path - strip off the server and sharename */
1095         int i, count;
1096         for ( i=2,count=2; count < 4 && firstp[i]; i++ ) {
1097             if ( firstp[i] == '\\' || firstp[i] == '/' ) {
1098                 count++;
1099             }
1100         }
1101         if ( firstp[i] == 0 ) {
1102             strcpy(outPathp,"\\");
1103         } else {
1104             strcpy(outPathp,&firstp[--i]);
1105         }
1106         for (p=outPathp ;*p; p++) {
1107             if (*p == '/')
1108                 *p = '\\';
1109         }
1110         return 0;
1111     } else if (firstp[0] == '\\' || firstp[0] == '/') {
1112         /* already an absolute pathname, just copy it back */
1113         strcpy(outPathp, firstp);
1114         for (p=outPathp ;*p; p++) {
1115             if (*p == '/')
1116                 *p = '\\';
1117         }
1118         return 0;
1119     }
1120
1121     GetCurrentDirectory(sizeof(origPath), origPath);
1122
1123     doSwitch = 0;
1124     if (pathHasDrive && (*pathp & ~0x20) != (origPath[0] & ~0x20)) {
1125         /* a drive has been specified and it isn't our current drive.
1126          * to get path, switch to it first.  Must case-fold drive letters
1127          * for user convenience.
1128          */
1129         doSwitch = 1;
1130         newPath[0] = *pathp;
1131         newPath[1] = ':';
1132         newPath[2] = 0;
1133         if (!SetCurrentDirectory(newPath)) {
1134             code = GetLastError();
1135
1136             if ( IoctlDebug() ) {
1137                 save = errno;
1138                 fprintf(stderr, "pioctl fs_GetFullPath SetCurrentDirectory(%s) failed: 0x%X\r\n",
1139                          newPath, code);
1140                 errno = save;
1141             }
1142             return code;
1143         }
1144     }
1145
1146     /* now get the absolute path to the current wdir in this drive */
1147     GetCurrentDirectory(sizeof(tpath), tpath);
1148     if (tpath[1] == ':')
1149         strcpy(outPathp, tpath + 2);    /* skip drive letter */
1150     else if ( tpath[0] == '\\' && tpath[1] == '\\'||
1151               tpath[0] == '/' && tpath[1] == '/') {
1152         /* UNC path - strip off the server and sharename */
1153         int i, count;
1154         for ( i=2,count=2; count < 4 && tpath[i]; i++ ) {
1155             if ( tpath[i] == '\\' || tpath[i] == '/' ) {
1156                 count++;
1157             }
1158         }
1159         if ( tpath[i] == 0 ) {
1160             strcpy(outPathp,"\\");
1161         } else {
1162             strcpy(outPathp,&tpath[--i]);
1163         }
1164     } else {
1165         /* this should never happen */
1166         strcpy(outPathp, tpath);
1167     }
1168
1169     /* if there is a non-null name after the drive, append it */
1170     if (*firstp != 0) {
1171         int len = (int)strlen(outPathp);
1172         if (outPathp[len-1] != '\\' && outPathp[len-1] != '/') 
1173             strcat(outPathp, "\\");
1174         strcat(outPathp, firstp);
1175     }
1176
1177     /* finally, if necessary, switch back to our home drive letter */
1178     if (doSwitch) {
1179         SetCurrentDirectory(origPath);
1180     }
1181
1182     for (p=outPathp ;*p; p++) {
1183         if (*p == '/')
1184             *p = '\\';
1185     }
1186     return 0;
1187 }
1188
1189 static int
1190 pioctl_int(char *pathp, afs_int32 opcode, struct ViceIoctl *blobp, afs_int32 follow, afs_int32 is_utf8)
1191 {
1192     fs_ioctlRequest_t preq;
1193     long code;
1194     long temp;
1195     char fullPath[1000];
1196     HANDLE reqHandle;
1197     int save;
1198
1199     code = GetIoctlHandle(pathp, &reqHandle);
1200     if (code) {
1201         if (pathp)
1202             errno = EINVAL;
1203         else
1204             errno = ENODEV;
1205         return code;
1206     }
1207
1208     /* init the request structure */
1209     InitFSRequest(&preq);
1210
1211     /* marshall the opcode, the path name and the input parameters */
1212     MarshallLong(&preq, opcode);
1213     /* when marshalling the path, remove the drive letter, since we already
1214      * used the drive letter to find the AFS daemon; we don't need it any more.
1215      * Eventually we'll expand relative path names here, too, since again, only
1216      * we understand those.
1217      */
1218     if (pathp) {
1219         code = fs_GetFullPath(pathp, fullPath, sizeof(fullPath));
1220         if (code) {
1221             CloseHandle(reqHandle);
1222             errno = EINVAL;
1223             return code;
1224         }
1225     } else {
1226         strcpy(fullPath, "");
1227     }
1228
1229     MarshallString(&preq, fullPath, is_utf8);
1230     if (blobp->in_size) {
1231         if (blobp->in_size > sizeof(preq.data) - (preq.mp - preq.data)*sizeof(char)) {
1232             errno = E2BIG;
1233             return -1;
1234         }
1235         memcpy(preq.mp, blobp->in, blobp->in_size);
1236         preq.mp += blobp->in_size;
1237     }
1238
1239     /* now make the call */
1240     code = Transceive(reqHandle, &preq);
1241     if (code) {
1242         CloseHandle(reqHandle);
1243         return code;
1244     }
1245
1246     /* now unmarshall the return value */
1247     if (UnmarshallLong(&preq, &temp) != 0) {
1248         CloseHandle(reqHandle);
1249         return -1;
1250     }
1251
1252     if (temp != 0) {
1253         CloseHandle(reqHandle);
1254         errno = CMtoUNIXerror(temp);
1255         if ( IoctlDebug() ) {
1256             save = errno;
1257             fprintf(stderr, "pioctl temp != 0: 0x%X\r\n",temp);
1258             errno = save;
1259         }
1260         return -1;
1261     }
1262
1263     /* otherwise, unmarshall the output parameters */
1264     if (blobp->out_size) {
1265         temp = blobp->out_size;
1266         if (preq.nbytes < temp)
1267             temp = preq.nbytes;
1268         memcpy(blobp->out, preq.mp, temp);
1269         blobp->out_size = temp;
1270     }
1271
1272     /* and return success */
1273     CloseHandle(reqHandle);
1274     return 0;
1275 }
1276
1277 int
1278 pioctl_utf8(char * pathp, afs_int32 opcode, struct ViceIoctl * blobp, afs_int32 follow)
1279 {
1280     return pioctl_int(pathp, opcode, blobp, follow, TRUE);
1281 }
1282
1283 int
1284 pioctl(char * pathp, afs_int32 opcode, struct ViceIoctl * blobp, afs_int32 follow)
1285 {
1286     return pioctl_int(pathp, opcode, blobp, follow, FALSE);
1287 }
1288