windows-pioctl-subst-unc-20090407
[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 //
401 // Recursively evaluate drivestr to find the final
402 // dos drive letter to which the source is mapped.
403 //
404 static BOOL
405 DriveSubstitution(char *drivestr, char *subststr, size_t substlen)
406 {
407     char device[MAX_PATH];
408
409     if ( QueryDosDevice(drivestr, device, MAX_PATH) )
410     {
411         if ( device[0] == '\\' &&
412              device[1] == '?' &&
413              device[2] == '?' &&
414              device[3] == '\\' &&
415              isalpha(device[4]) &&
416              device[5] == ':')
417         {
418             device[0] = device[4];
419             device[1] = ':';
420             device[2] = '\0';
421             if ( DriveSubstitution(device, subststr, substlen) )
422             {
423                 return TRUE;
424             } else {
425                 subststr[0] = device[0];
426                 subststr[1] = ':';
427                 subststr[2] = '\0';
428                 return TRUE;
429             }
430         } else 
431         if ( device[0] == '\\' &&
432              device[1] == '?' &&
433              device[2] == '?' &&
434              device[3] == '\\' &&
435              device[4] == 'U' &&
436              device[5] == 'N' &&
437              device[6] == 'C' &&
438              device[7] == '\\')
439         {
440              subststr[0] = '\\';
441              strncpy(&subststr[1], &device[7], substlen-1);
442              subststr[substlen-1] = '\0';
443              return TRUE;
444         }
445     }
446
447     return FALSE;
448 }
449
450 // 
451 // drivestr - is "<drive-letter>:"
452 //
453 static BOOL
454 DriveIsMappedToAFS(char *drivestr, char *NetbiosName)
455 {
456     DWORD dwResult, dwResultEnum;
457     HANDLE hEnum;
458     DWORD cbBuffer = 16384;     // 16K is a good size
459     DWORD cEntries = -1;        // enumerate all possible entries
460     LPNETRESOURCE lpnrLocal;    // pointer to enumerated structures
461     DWORD i;
462     BOOL  bIsAFS = FALSE;
463     char  subststr[MAX_PATH];
464
465     //
466     // Handle drive letter substitution created with "SUBST <drive> <path>".
467     // If a substitution has occurred, use the target drive letter instead
468     // of the source.
469     //
470     if ( DriveSubstitution(drivestr, subststr, MAX_PATH) )
471     {
472         if (subststr[0] == '\\' &&
473             subststr[1] == '\\') 
474         {
475             if (_strnicmp( &subststr[2], NetbiosName, strlen(NetbiosName)) == 0)
476                 return TRUE;
477             else
478                 return FALSE;
479         }
480         drivestr = subststr;
481     }
482
483     //
484     // Call the WNetOpenEnum function to begin the enumeration.
485     //
486     dwResult = WNetOpenEnum(RESOURCE_CONNECTED,
487                             RESOURCETYPE_DISK,
488                             RESOURCEUSAGE_ALL,
489                             NULL,       // NULL first time the function is called
490                             &hEnum);    // handle to the resource
491
492     if (dwResult != NO_ERROR)
493         return FALSE;
494
495     //
496     // Call the GlobalAlloc function to allocate resources.
497     //
498     lpnrLocal = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer);
499     if (lpnrLocal == NULL)
500         return FALSE;
501
502     do {
503         //
504         // Initialize the buffer.
505         //
506         ZeroMemory(lpnrLocal, cbBuffer);
507         //
508         // Call the WNetEnumResource function to continue
509         //  the enumeration.
510         //
511         cEntries = -1;
512         dwResultEnum = WNetEnumResource(hEnum,          // resource handle
513                                         &cEntries,      // defined locally as -1
514                                         lpnrLocal,      // LPNETRESOURCE
515                                         &cbBuffer);     // buffer size
516         //
517         // If the call succeeds, loop through the structures.
518         //
519         if (dwResultEnum == NO_ERROR) {
520             for (i = 0; i < cEntries; i++) {
521                 if (lpnrLocal[i].lpLocalName &&
522                     toupper(lpnrLocal[i].lpLocalName[0]) == toupper(drivestr[0])) {
523                     //
524                     // Skip the two backslashes at the start of the UNC device name
525                     //
526                     if ( _strnicmp( &(lpnrLocal[i].lpRemoteName[2]), NetbiosName, strlen(NetbiosName)) == 0 )
527                     {
528                         bIsAFS = TRUE;
529                         break;
530                     }
531                 }
532             }
533         }
534         // Process errors.
535         //
536         else if (dwResultEnum != ERROR_NO_MORE_ITEMS)
537             break;
538     }
539     while (dwResultEnum != ERROR_NO_MORE_ITEMS);
540     
541     //
542     // Call the GlobalFree function to free the memory.
543     //
544     GlobalFree((HGLOBAL) lpnrLocal);
545     //
546     // Call WNetCloseEnum to end the enumeration.
547     //
548     dwResult = WNetCloseEnum(hEnum);
549
550     return bIsAFS;
551 }
552
553 static BOOL
554 DriveIsGlobalAutoMapped(char *drivestr)
555 {
556     DWORD dwResult;
557     HKEY hKey;
558     DWORD dwSubMountSize;
559     char szSubMount[260];
560     DWORD dwType;
561
562     dwResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
563                             AFSREG_CLT_SVC_PARAM_SUBKEY "\\GlobalAutoMapper", 
564                             0, KEY_QUERY_VALUE, &hKey);
565     if (dwResult != ERROR_SUCCESS)
566         return FALSE;
567
568     dwSubMountSize = sizeof(szSubMount);
569     dwType = REG_SZ;
570     dwResult = RegQueryValueEx(hKey, drivestr, 0, &dwType, szSubMount, &dwSubMountSize);
571     RegCloseKey(hKey);
572
573     if (dwResult == ERROR_SUCCESS && dwType == REG_SZ)
574         return TRUE;
575     else
576         return FALSE;
577 }
578
579 static long
580 GetIoctlHandle(char *fileNamep, HANDLE * handlep)
581 {
582     char *drivep = NULL;
583     char netbiosName[MAX_NB_NAME_LENGTH];
584     DWORD CurrentState = 0;
585     char  HostName[64] = "";
586     char tbuffer[MAX_PATH]="";
587     HANDLE fh;
588     HKEY hk;
589     char szUser[128] = "";
590     char szClient[MAX_PATH] = "";
591     char szPath[MAX_PATH] = "";
592     NETRESOURCE nr;
593     DWORD res;
594     DWORD ioctlDebug = IoctlDebug();
595     DWORD gle;
596     DWORD dwSize = sizeof(szUser);
597     int saveerrno;
598     UINT driveType;
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     fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
701                     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
702                     FILE_FLAG_WRITE_THROUGH, NULL);
703
704         fflush(stdout);
705
706     if (fh == INVALID_HANDLE_VALUE) {
707         int  gonext = 0;
708
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         lana_GetNetbiosName(szClient, LANA_NETBIOS_NAME_FULL);
730
731         if (RegOpenKey (HKEY_CURRENT_USER, 
732                          TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), &hk) == 0)
733         {
734             DWORD dwType = REG_SZ;
735             RegQueryValueEx (hk, TEXT("Logon User Name"), NULL, &dwType, (PBYTE)szUser, &dwSize);
736             RegCloseKey (hk);
737         }
738
739         if ( szUser[0] ) {
740             if ( ioctlDebug ) {
741                 saveerrno = errno;
742                 fprintf(stderr, "pioctl Explorer logon user: [%s]\r\n",szUser);
743                 errno = saveerrno;
744             }
745             sprintf(szPath, "\\\\%s", szClient);
746             memset (&nr, 0x00, sizeof(NETRESOURCE));
747             nr.dwType=RESOURCETYPE_DISK;
748             nr.lpLocalName=0;
749             nr.lpRemoteName=szPath;
750             res = WNetAddConnection2(&nr,NULL,szUser,0);
751             if (res) {
752                 if ( ioctlDebug ) {
753                     saveerrno = errno;
754                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
755                              szPath,szUser,res);
756                     errno = saveerrno;
757                 }
758                 gonext = 1;
759             }
760
761             sprintf(szPath, "\\\\%s\\all", szClient);
762             res = WNetAddConnection2(&nr,NULL,szUser,0);
763             if (res) {
764                 if ( ioctlDebug ) {
765                     saveerrno = errno;
766                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
767                              szPath,szUser,res);
768                     errno = saveerrno;
769                 }
770                 gonext = 1;
771             }
772
773             if (gonext)
774                 goto try_lsa_principal;
775
776             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
777                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
778                              FILE_FLAG_WRITE_THROUGH, NULL);
779             fflush(stdout);
780             if (fh == INVALID_HANDLE_VALUE) {
781                 gle = GetLastError();
782                 if (gle && ioctlDebug ) {
783                     char buf[4096];
784
785                     saveerrno = errno;
786                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
787                                         NULL,
788                                         gle,
789                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
790                                         buf,
791                                         4096,
792                                         (va_list *) NULL
793                                         ) )
794                     {
795                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
796                                  tbuffer,gle,buf);
797                     }
798                     errno = saveerrno;
799                 }
800             }
801         }
802     }
803
804   try_lsa_principal:
805     if (fh == INVALID_HANDLE_VALUE) {
806         int  gonext = 0;
807
808         dwSize = sizeof(szUser);
809         if (GetLSAPrincipalName(szUser, &dwSize)) {
810             if ( ioctlDebug ) {
811                 saveerrno = errno;
812                 fprintf(stderr, "pioctl LSA Principal logon user: [%s]\r\n",szUser);
813                 errno = saveerrno;
814             }
815             sprintf(szPath, "\\\\%s", szClient);
816             memset (&nr, 0x00, sizeof(NETRESOURCE));
817             nr.dwType=RESOURCETYPE_DISK;
818             nr.lpLocalName=0;
819             nr.lpRemoteName=szPath;
820             res = WNetAddConnection2(&nr,NULL,szUser,0);
821             if (res) {
822                 if ( ioctlDebug ) {
823                     saveerrno = errno;
824                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
825                              szPath,szUser,res);
826                     errno = saveerrno;
827                 }
828                 gonext = 1;
829             }
830
831             sprintf(szPath, "\\\\%s\\all", szClient);
832             res = WNetAddConnection2(&nr,NULL,szUser,0);
833             if (res) {
834                 if ( ioctlDebug ) {
835                     saveerrno = errno;
836                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
837                              szPath,szUser,res);
838                     errno = saveerrno;
839                 }
840                 gonext = 1;
841             }
842
843             if (gonext)
844                 goto try_sam_compat;
845
846             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
847                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
848                              FILE_FLAG_WRITE_THROUGH, NULL);
849             fflush(stdout);
850             if (fh == INVALID_HANDLE_VALUE) {
851                 gle = GetLastError();
852                 if (gle && ioctlDebug ) {
853                     char buf[4096];
854
855                     saveerrno = errno;
856                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
857                                         NULL,
858                                         gle,
859                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
860                                         buf,
861                                         4096,
862                                         (va_list *) NULL
863                                         ) )
864                     {
865                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
866                                  tbuffer,gle,buf);
867                     }
868                     errno = saveerrno;
869
870                 }
871             }
872         }
873     }
874
875   try_sam_compat:
876     if ( fh == INVALID_HANDLE_VALUE ) {
877         dwSize = sizeof(szUser);
878         if (GetUserNameEx(NameSamCompatible, szUser, &dwSize)) {
879             if ( ioctlDebug ) {
880                 saveerrno = errno;
881                 fprintf(stderr, "pioctl SamCompatible logon user: [%s]\r\n",szUser);
882                 errno = saveerrno;
883             }
884             sprintf(szPath, "\\\\%s", szClient);
885             memset (&nr, 0x00, sizeof(NETRESOURCE));
886             nr.dwType=RESOURCETYPE_DISK;
887             nr.lpLocalName=0;
888             nr.lpRemoteName=szPath;
889             res = WNetAddConnection2(&nr,NULL,szUser,0);
890             if (res) {
891                 if ( ioctlDebug ) {
892                     saveerrno = errno;
893                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
894                              szPath,szUser,res);
895                     errno = saveerrno;
896                 }
897             }
898
899             sprintf(szPath, "\\\\%s\\all", szClient);
900             res = WNetAddConnection2(&nr,NULL,szUser,0);
901             if (res) {
902                 if ( ioctlDebug ) {
903                     saveerrno = errno;
904                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
905                              szPath,szUser,res);
906                     errno = saveerrno;
907                 }
908                 return -1;
909             }
910
911             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
912                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
913                              FILE_FLAG_WRITE_THROUGH, NULL);
914             fflush(stdout);
915             if (fh == INVALID_HANDLE_VALUE) {
916                 gle = GetLastError();
917                 if (gle && ioctlDebug ) {
918                     char buf[4096];
919
920                     saveerrno = errno;
921                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
922                                         NULL,
923                                         gle,
924                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
925                                         buf,
926                                         4096,
927                                         (va_list *) NULL
928                                         ) )
929                     {
930                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
931                                  tbuffer,gle,buf);
932                     }
933                     errno = saveerrno;
934                 }
935                 return -1;
936             }
937         } else {
938             fprintf(stderr, "GetUserNameEx(NameSamCompatible) failed: 0x%X\r\n", GetLastError());
939             return -1;
940         }
941     }
942
943     /* return fh and success code */
944     *handlep = fh;
945     return 0;
946 }
947
948 static long
949 Transceive(HANDLE handle, fs_ioctlRequest_t * reqp)
950 {
951     long rcount;
952     long ioCount;
953     DWORD gle;
954     DWORD ioctlDebug = IoctlDebug();
955     int save;
956
957     rcount = (long)(reqp->mp - reqp->data);
958     if (rcount <= 0) {
959         if ( ioctlDebug ) {
960             save = errno;
961             fprintf(stderr, "pioctl Transceive rcount <= 0: %d\r\n",rcount);
962             errno = save;
963         }
964         return EINVAL;          /* not supposed to happen */
965     }
966
967     if (!WriteFile(handle, reqp->data, rcount, &ioCount, NULL)) {
968         /* failed to write */
969         gle = GetLastError();
970
971         if ( ioctlDebug ) {
972             save = errno;
973             fprintf(stderr, "pioctl Transceive WriteFile failed: 0x%X\r\n",gle);
974             errno = save;
975         }
976         return gle;
977     }
978
979     if (!ReadFile(handle, reqp->data, sizeof(reqp->data), &ioCount, NULL)) {
980         /* failed to read */
981         gle = GetLastError();
982
983         if ( ioctlDebug ) {
984             save = errno;
985             fprintf(stderr, "pioctl Transceive ReadFile failed: 0x%X\r\n",gle);
986             errno = save;
987         }
988         return gle;
989     }
990
991     reqp->nbytes = ioCount;     /* set # of bytes available */
992     reqp->mp = reqp->data;      /* restart marshalling */
993
994     /* return success */
995     return 0;
996 }
997
998 static long
999 MarshallLong(fs_ioctlRequest_t * reqp, long val)
1000 {
1001     memcpy(reqp->mp, &val, 4);
1002     reqp->mp += 4;
1003     return 0;
1004 }
1005
1006 static long
1007 UnmarshallLong(fs_ioctlRequest_t * reqp, long *valp)
1008 {
1009     int save;
1010
1011     /* not enough data left */
1012     if (reqp->nbytes < 4) {
1013         if ( IoctlDebug() ) {
1014             save = errno;
1015             fprintf(stderr, "pioctl UnmarshallLong reqp->nbytes < 4: %d\r\n",
1016                      reqp->nbytes);
1017             errno = save;
1018         }
1019         return -1;
1020     }
1021
1022     memcpy(valp, reqp->mp, 4);
1023     reqp->mp += 4;
1024     reqp->nbytes -= 4;
1025     return 0;
1026 }
1027
1028 /* includes marshalling NULL pointer as a null (0 length) string */
1029 static long
1030 MarshallString(fs_ioctlRequest_t * reqp, char *stringp, int is_utf8)
1031 {
1032     int count;
1033     int save;
1034
1035     if (stringp)
1036         count = (int)strlen(stringp) + 1;/* space required including null */
1037     else
1038         count = 1;
1039
1040     if (is_utf8) {
1041         count += utf8_prefix_size;
1042     }
1043
1044     /* watch for buffer overflow */
1045     if ((reqp->mp - reqp->data) + count > sizeof(reqp->data)) {
1046         if ( IoctlDebug() ) {
1047             save = errno;
1048             fprintf(stderr, "pioctl MarshallString buffer overflow\r\n");
1049             errno = save;
1050         }
1051         return -1;
1052     }
1053
1054     if (is_utf8) {
1055         memcpy(reqp->mp, utf8_prefix, utf8_prefix_size);
1056         reqp->mp += utf8_prefix_size;
1057         count -= utf8_prefix_size;
1058     }
1059
1060     if (stringp)
1061         memcpy(reqp->mp, stringp, count);
1062     else
1063         *(reqp->mp) = 0;
1064     reqp->mp += count;
1065     return 0;
1066 }
1067
1068 /* take a path with a drive letter, possibly relative, and return a full path
1069  * without the drive letter.  This is the full path relative to the working
1070  * dir for that drive letter.  The input and output paths can be the same.
1071  */
1072 static long
1073 fs_GetFullPath(char *pathp, char *outPathp, long outSize)
1074 {
1075     char tpath[1000];
1076     char origPath[1000];
1077     char *firstp;
1078     long code;
1079     int pathHasDrive;
1080     int doSwitch;
1081     char newPath[3];
1082     char * p;
1083     int save;
1084
1085     if (pathp[0] != 0 && pathp[1] == ':') {
1086         /* there's a drive letter there */
1087         firstp = pathp + 2;
1088         pathHasDrive = 1;
1089     } else {
1090         firstp = pathp;
1091         pathHasDrive = 0;
1092     }
1093
1094     if ( firstp[0] == '\\' && firstp[1] == '\\' || 
1095          firstp[0] == '/' && firstp[1] == '/') {
1096         /* UNC path - strip off the server and sharename */
1097         int i, count;
1098         for ( i=2,count=2; count < 4 && firstp[i]; i++ ) {
1099             if ( firstp[i] == '\\' || firstp[i] == '/' ) {
1100                 count++;
1101             }
1102         }
1103         if ( firstp[i] == 0 ) {
1104             strcpy(outPathp,"\\");
1105         } else {
1106             strcpy(outPathp,&firstp[--i]);
1107         }
1108         for (p=outPathp ;*p; p++) {
1109             if (*p == '/')
1110                 *p = '\\';
1111         }
1112         return 0;
1113     } else if (firstp[0] == '\\' || firstp[0] == '/') {
1114         /* already an absolute pathname, just copy it back */
1115         strcpy(outPathp, firstp);
1116         for (p=outPathp ;*p; p++) {
1117             if (*p == '/')
1118                 *p = '\\';
1119         }
1120         return 0;
1121     }
1122
1123     GetCurrentDirectory(sizeof(origPath), origPath);
1124
1125     doSwitch = 0;
1126     if (pathHasDrive && (*pathp & ~0x20) != (origPath[0] & ~0x20)) {
1127         /* a drive has been specified and it isn't our current drive.
1128          * to get path, switch to it first.  Must case-fold drive letters
1129          * for user convenience.
1130          */
1131         doSwitch = 1;
1132         newPath[0] = *pathp;
1133         newPath[1] = ':';
1134         newPath[2] = 0;
1135         if (!SetCurrentDirectory(newPath)) {
1136             code = GetLastError();
1137
1138             if ( IoctlDebug() ) {
1139                 save = errno;
1140                 fprintf(stderr, "pioctl fs_GetFullPath SetCurrentDirectory(%s) failed: 0x%X\r\n",
1141                          newPath, code);
1142                 errno = save;
1143             }
1144             return code;
1145         }
1146     }
1147
1148     /* now get the absolute path to the current wdir in this drive */
1149     GetCurrentDirectory(sizeof(tpath), tpath);
1150     if (tpath[1] == ':')
1151         strcpy(outPathp, tpath + 2);    /* skip drive letter */
1152     else if ( tpath[0] == '\\' && tpath[1] == '\\'||
1153               tpath[0] == '/' && tpath[1] == '/') {
1154         /* UNC path - strip off the server and sharename */
1155         int i, count;
1156         for ( i=2,count=2; count < 4 && tpath[i]; i++ ) {
1157             if ( tpath[i] == '\\' || tpath[i] == '/' ) {
1158                 count++;
1159             }
1160         }
1161         if ( tpath[i] == 0 ) {
1162             strcpy(outPathp,"\\");
1163         } else {
1164             strcpy(outPathp,&tpath[--i]);
1165         }
1166     } else {
1167         /* this should never happen */
1168         strcpy(outPathp, tpath);
1169     }
1170
1171     /* if there is a non-null name after the drive, append it */
1172     if (*firstp != 0) {
1173         int len = (int)strlen(outPathp);
1174         if (outPathp[len-1] != '\\' && outPathp[len-1] != '/') 
1175             strcat(outPathp, "\\");
1176         strcat(outPathp, firstp);
1177     }
1178
1179     /* finally, if necessary, switch back to our home drive letter */
1180     if (doSwitch) {
1181         SetCurrentDirectory(origPath);
1182     }
1183
1184     for (p=outPathp ;*p; p++) {
1185         if (*p == '/')
1186             *p = '\\';
1187     }
1188     return 0;
1189 }
1190
1191 static long
1192 pioctl_int(char *pathp, long opcode, struct ViceIoctl *blobp, int follow, int is_utf8)
1193 {
1194     fs_ioctlRequest_t preq;
1195     long code;
1196     long temp;
1197     char fullPath[1000];
1198     HANDLE reqHandle;
1199     int save;
1200
1201     code = GetIoctlHandle(pathp, &reqHandle);
1202     if (code) {
1203         if (pathp)
1204             errno = EINVAL;
1205         else
1206             errno = ENODEV;
1207         return code;
1208     }
1209
1210     /* init the request structure */
1211     InitFSRequest(&preq);
1212
1213     /* marshall the opcode, the path name and the input parameters */
1214     MarshallLong(&preq, opcode);
1215     /* when marshalling the path, remove the drive letter, since we already
1216      * used the drive letter to find the AFS daemon; we don't need it any more.
1217      * Eventually we'll expand relative path names here, too, since again, only
1218      * we understand those.
1219      */
1220     if (pathp) {
1221         code = fs_GetFullPath(pathp, fullPath, sizeof(fullPath));
1222         if (code) {
1223             CloseHandle(reqHandle);
1224             errno = EINVAL;
1225             return code;
1226         }
1227     } else {
1228         strcpy(fullPath, "");
1229     }
1230
1231     MarshallString(&preq, fullPath, is_utf8);
1232     if (blobp->in_size) {
1233         if (blobp->in_size > sizeof(preq.data) - (preq.mp - preq.data)*sizeof(char)) {
1234             errno = E2BIG;
1235             return -1;
1236         }
1237         memcpy(preq.mp, blobp->in, blobp->in_size);
1238         preq.mp += blobp->in_size;
1239     }
1240
1241     /* now make the call */
1242     code = Transceive(reqHandle, &preq);
1243     if (code) {
1244         CloseHandle(reqHandle);
1245         return code;
1246     }
1247
1248     /* now unmarshall the return value */
1249     if (UnmarshallLong(&preq, &temp) != 0) {
1250         CloseHandle(reqHandle);
1251         return -1;
1252     }
1253
1254     if (temp != 0) {
1255         CloseHandle(reqHandle);
1256         errno = CMtoUNIXerror(temp);
1257         if ( IoctlDebug() ) {
1258             save = errno;
1259             fprintf(stderr, "pioctl temp != 0: 0x%X\r\n",temp);
1260             errno = save;
1261         }
1262         return -1;
1263     }
1264
1265     /* otherwise, unmarshall the output parameters */
1266     if (blobp->out_size) {
1267         temp = blobp->out_size;
1268         if (preq.nbytes < temp)
1269             temp = preq.nbytes;
1270         memcpy(blobp->out, preq.mp, temp);
1271         blobp->out_size = temp;
1272     }
1273
1274     /* and return success */
1275     CloseHandle(reqHandle);
1276     return 0;
1277 }
1278
1279 long
1280 pioctl_utf8(char * pathp, long opcode, struct ViceIoctl * blobp, int follow)
1281 {
1282     return pioctl_int(pathp, opcode, blobp, follow, TRUE);
1283 }
1284
1285 long
1286 pioctl(char * pathp, long opcode, struct ViceIoctl * blobp, int follow)
1287 {
1288     return pioctl_int(pathp, opcode, blobp, follow, FALSE);
1289 }
1290