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