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