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