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