windows-remove-umich-afsifs-20080318
[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_server.h>
33 #include <cm_cell.h>
34 #include <cm_user.h>
35 #include <cm_conn.h>
36 #include <cm_scache.h>
37 #include <cm_buf.h>
38 #include <cm_dir.h>
39 #include <cm_utils.h>
40 #include <cm_ioctl.h>
41
42 #include <smb.h>
43 #include <pioctl_nt.h>
44 #include <WINNT/afsreg.h>
45 #include <lanahelper.h>
46 #include <../WINNT/afsrdr/kif.h>
47
48 #include <loadfuncs-krb5.h>
49 #include <krb5.h>
50
51 static char AFSConfigKeyName[] = AFSREG_CLT_SVC_PARAM_SUBKEY;
52
53 #define FS_IOCTLREQUEST_MAXSIZE 8192
54 /* big structure for representing and storing an IOCTL request */
55 typedef struct fs_ioctlRequest {
56     char *mp;                   /* marshalling/unmarshalling ptr */
57     long nbytes;                /* bytes received (when unmarshalling) */
58     char data[FS_IOCTLREQUEST_MAXSIZE]; /* data we're marshalling */
59 } fs_ioctlRequest_t;
60
61 static int
62 CMtoUNIXerror(int cm_code)
63 {
64     switch (cm_code) {
65     case CM_ERROR_TIMEDOUT:
66         return ETIMEDOUT;
67     case CM_ERROR_NOACCESS:
68         return EACCES;
69     case CM_ERROR_NOSUCHFILE:
70     case CM_ERROR_NOSUCHPATH:
71     case CM_ERROR_BPLUS_NOMATCH:
72         return ENOENT;
73     case CM_ERROR_INVAL:
74         return EINVAL;
75     case CM_ERROR_BADFD:
76         return EBADF;
77     case CM_ERROR_EXISTS:
78     case CM_ERROR_INEXACT_MATCH:
79         return EEXIST;
80     case CM_ERROR_CROSSDEVLINK:
81         return EXDEV;
82     case CM_ERROR_NOTDIR:
83         return ENOTDIR;
84     case CM_ERROR_ISDIR:
85         return EISDIR;
86     case CM_ERROR_READONLY:
87         return EROFS;
88     case CM_ERROR_WOULDBLOCK:
89         return EWOULDBLOCK;
90     case CM_ERROR_NOSUCHCELL:
91         return ESRCH;           /* hack */
92     case CM_ERROR_NOSUCHVOLUME:
93         return EPIPE;           /* hack */
94     case CM_ERROR_NOMORETOKENS:
95         return EDOM;            /* hack */
96     case CM_ERROR_TOOMANYBUFS:
97         return EFBIG;           /* hack */
98     case CM_ERROR_ALLBUSY:
99         return EBUSY;
100     case CM_ERROR_ALLDOWN:
101         return ENOSYS;          /* hack */
102     case CM_ERROR_ALLOFFLINE:
103         return ENXIO;           /* hack */
104     default:
105         if (cm_code > 0 && cm_code < EILSEQ)
106             return cm_code;
107         else
108             return ENOTTY;
109     }
110 }
111
112 static void
113 InitFSRequest(fs_ioctlRequest_t * rp)
114 {
115     rp->mp = rp->data;
116     rp->nbytes = 0;
117 }
118
119 static BOOL
120 IoctlDebug(void)
121 {
122     static int init = 0;
123     static BOOL debug = 0;
124
125     if ( !init ) {
126         HKEY hk;
127
128         if (RegOpenKey (HKEY_LOCAL_MACHINE, 
129                          TEXT("Software\\OpenAFS\\Client"), &hk) == 0)
130         {
131             DWORD dwSize = sizeof(BOOL);
132             DWORD dwType = REG_DWORD;
133             RegQueryValueEx (hk, TEXT("IoctlDebug"), NULL, &dwType, (PBYTE)&debug, &dwSize);
134             RegCloseKey (hk);
135         }
136
137         init = 1;
138     }
139
140     return debug;
141 }
142
143 static DWORD 
144 GetServiceStatus(
145     LPSTR lpszMachineName, 
146     LPSTR lpszServiceName,
147     DWORD *lpdwCurrentState) 
148
149     DWORD           hr               = NOERROR; 
150     SC_HANDLE       schSCManager     = NULL; 
151     SC_HANDLE       schService       = NULL; 
152     DWORD           fdwDesiredAccess = 0; 
153     SERVICE_STATUS  ssServiceStatus  = {0}; 
154     BOOL            fRet             = FALSE; 
155
156     *lpdwCurrentState = 0; 
157  
158     fdwDesiredAccess = GENERIC_READ; 
159  
160     schSCManager = OpenSCManager(lpszMachineName,  
161                                  NULL,
162                                  fdwDesiredAccess); 
163  
164     if(schSCManager == NULL) 
165     { 
166         hr = GetLastError();
167         goto cleanup; 
168     } 
169  
170     schService = OpenService(schSCManager,
171                              lpszServiceName,
172                              fdwDesiredAccess); 
173  
174     if(schService == NULL) 
175     { 
176         hr = GetLastError();
177         goto cleanup; 
178     } 
179  
180     fRet = QueryServiceStatus(schService,
181                               &ssServiceStatus); 
182  
183     if(fRet == FALSE) 
184     { 
185         hr = GetLastError(); 
186         goto cleanup; 
187     } 
188  
189     *lpdwCurrentState = ssServiceStatus.dwCurrentState; 
190  
191 cleanup: 
192  
193     CloseServiceHandle(schService); 
194     CloseServiceHandle(schSCManager); 
195  
196     return(hr); 
197
198
199 // krb5 functions
200 DECL_FUNC_PTR(krb5_cc_default_name);
201 DECL_FUNC_PTR(krb5_cc_set_default_name);
202 DECL_FUNC_PTR(krb5_get_default_config_files);
203 DECL_FUNC_PTR(krb5_free_config_files);
204 DECL_FUNC_PTR(krb5_free_context);
205 DECL_FUNC_PTR(krb5_get_default_realm);
206 DECL_FUNC_PTR(krb5_free_default_realm);
207 DECL_FUNC_PTR(krb5_init_context);
208 DECL_FUNC_PTR(krb5_cc_default);
209 DECL_FUNC_PTR(krb5_parse_name);
210 DECL_FUNC_PTR(krb5_free_principal);
211 DECL_FUNC_PTR(krb5_cc_close);
212 DECL_FUNC_PTR(krb5_cc_get_principal);
213 DECL_FUNC_PTR(krb5_build_principal);
214 DECL_FUNC_PTR(krb5_c_random_make_octets);
215 DECL_FUNC_PTR(krb5_get_init_creds_password);
216 DECL_FUNC_PTR(krb5_free_cred_contents);
217 DECL_FUNC_PTR(krb5_cc_resolve);
218 DECL_FUNC_PTR(krb5_unparse_name);
219 DECL_FUNC_PTR(krb5_free_unparsed_name);
220
221 FUNC_INFO krb5_fi[] = {
222     MAKE_FUNC_INFO(krb5_cc_default_name),
223     MAKE_FUNC_INFO(krb5_cc_set_default_name),
224     MAKE_FUNC_INFO(krb5_get_default_config_files),
225     MAKE_FUNC_INFO(krb5_free_config_files),
226     MAKE_FUNC_INFO(krb5_free_context),
227     MAKE_FUNC_INFO(krb5_get_default_realm),
228     MAKE_FUNC_INFO(krb5_free_default_realm),
229     MAKE_FUNC_INFO(krb5_init_context),
230     MAKE_FUNC_INFO(krb5_cc_default),
231     MAKE_FUNC_INFO(krb5_parse_name),
232     MAKE_FUNC_INFO(krb5_free_principal),
233     MAKE_FUNC_INFO(krb5_cc_close),
234     MAKE_FUNC_INFO(krb5_cc_get_principal),
235     MAKE_FUNC_INFO(krb5_build_principal),
236     MAKE_FUNC_INFO(krb5_c_random_make_octets),
237     MAKE_FUNC_INFO(krb5_get_init_creds_password),
238     MAKE_FUNC_INFO(krb5_free_cred_contents),
239     MAKE_FUNC_INFO(krb5_cc_resolve),
240     MAKE_FUNC_INFO(krb5_unparse_name),
241     MAKE_FUNC_INFO(krb5_free_unparsed_name),
242     END_FUNC_INFO
243 };
244
245 static int
246 LoadFuncs(
247     const char* dll_name,
248     FUNC_INFO fi[],
249     HINSTANCE* ph,  // [out, optional] - DLL handle
250     int* pindex,    // [out, optional] - index of last func loaded (-1 if none)
251     int cleanup,    // cleanup function pointers and unload on error
252     int go_on,      // continue loading even if some functions cannot be loaded
253     int silent      // do not pop-up a system dialog if DLL cannot be loaded
254     )
255 {
256     HINSTANCE h;
257     int i, n, last_i;
258     int error = 0;
259     UINT em;
260
261     if (ph) *ph = 0;
262     if (pindex) *pindex = -1;
263
264     for (n = 0; fi[n].func_ptr_var; n++)
265         *(fi[n].func_ptr_var) = 0;
266
267     if (silent)
268         em = SetErrorMode(SEM_FAILCRITICALERRORS);
269     h = LoadLibrary(dll_name);
270     if (silent)
271         SetErrorMode(em);
272
273     if (!h)
274         return 0;
275
276     last_i = -1;
277     for (i = 0; (go_on || !error) && (i < n); i++)
278     {
279         void* p = (void*)GetProcAddress(h, fi[i].func_name);
280         if (!p)
281             error = 1;
282         else
283         {
284             last_i = i;
285             *(fi[i].func_ptr_var) = p;
286         }
287     }
288     if (pindex) *pindex = last_i;
289     if (error && cleanup && !go_on) {
290         for (i = 0; i < n; i++) {
291             *(fi[i].func_ptr_var) = 0;
292         }
293         FreeLibrary(h);
294         return 0;
295     }
296     if (ph) *ph = h;
297     if (error) return 0;
298     return 1;
299 }
300 #if defined(_IA64_) || defined(_AMD64_)
301 #define KERB5DLL "krb5_64.dll"
302 #else
303 #define KERB5DLL "krb5_32.dll"
304 #endif
305 static BOOL
306 IsKrb5Available()
307 {
308     static HINSTANCE hKrb5DLL = 0;
309
310     if ( hKrb5DLL )
311         return TRUE;
312
313     hKrb5DLL = LoadLibrary(KERB5DLL);
314     if (hKrb5DLL) {
315         if (!LoadFuncs(KERB5DLL, krb5_fi, 0, 0, 1, 0, 0))
316         {
317             FreeLibrary(hKrb5DLL);
318             hKrb5DLL = 0;
319             return FALSE;
320         }
321         return TRUE;
322     }
323     return FALSE;
324 }
325
326 static BOOL
327 GetLSAPrincipalName(char * szUser, DWORD *dwSize)
328 {
329     krb5_context   ctx = 0;
330     krb5_error_code code;
331     krb5_ccache mslsa_ccache=0;
332     krb5_principal princ = 0;
333     char * pname = 0;
334     BOOL success = 0;
335
336     if (!IsKrb5Available())
337         return FALSE;
338
339     if (code = pkrb5_init_context(&ctx))
340         goto cleanup;
341
342     if (code = pkrb5_cc_resolve(ctx, "MSLSA:", &mslsa_ccache))
343         goto cleanup;
344
345     if (code = pkrb5_cc_get_principal(ctx, mslsa_ccache, &princ))
346         goto cleanup;
347
348     if (code = pkrb5_unparse_name(ctx, princ, &pname))
349         goto cleanup;
350
351     if ( strlen(pname) < *dwSize ) {
352         strncpy(szUser, pname, *dwSize);
353         szUser[*dwSize-1] = '\0';
354         success = 1;
355     }
356     *dwSize = (DWORD)strlen(pname);
357
358   cleanup:
359     if (pname)
360         pkrb5_free_unparsed_name(ctx, pname);
361
362     if (princ)
363         pkrb5_free_principal(ctx, princ);
364
365     if (mslsa_ccache)
366         pkrb5_cc_close(ctx, mslsa_ccache);
367
368     if (ctx)
369         pkrb5_free_context(ctx);
370     return success;
371 }
372
373 static long
374 GetIoctlHandle(char *fileNamep, HANDLE * handlep)
375 {
376     char *drivep;
377     char netbiosName[MAX_NB_NAME_LENGTH];
378     DWORD CurrentState = 0;
379     char  HostName[64] = "";
380     char tbuffer[256]="";
381     HANDLE fh;
382     HKEY hk;
383     char szUser[128] = "";
384     char szClient[MAX_PATH] = "";
385     char szPath[MAX_PATH] = "";
386     NETRESOURCE nr;
387     DWORD res;
388     DWORD ioctlDebug = IoctlDebug();
389     DWORD gle;
390     DWORD dwSize = sizeof(szUser);
391
392     memset(HostName, '\0', sizeof(HostName));
393     gethostname(HostName, sizeof(HostName));
394     if (GetServiceStatus(HostName, TEXT("TransarcAFSDaemon"), &CurrentState) == NOERROR &&
395         CurrentState != SERVICE_RUNNING)
396         return -1;
397
398     if (fileNamep) {
399         drivep = strchr(fileNamep, ':');
400         if (drivep && (drivep - fileNamep) >= 1) {
401             tbuffer[0] = *(drivep - 1);
402             tbuffer[1] = ':';
403             strcpy(tbuffer + 2, SMB_IOCTL_FILENAME);
404         } else if (fileNamep[0] == fileNamep[1] && 
405                    (fileNamep[0] == '\\' || fileNamep[0] == '/'))
406         {
407             int count = 0, i = 0;
408
409             while (count < 4 && fileNamep[i]) {
410                 tbuffer[i] = fileNamep[i];
411                 if ( tbuffer[i] == '\\' ||
412                      tbuffer[i] == '/')
413                     count++;
414                 i++;
415             }
416             if (fileNamep[i] == 0)
417                 tbuffer[i++] = '\\';
418             tbuffer[i] = 0;
419             strcat(tbuffer, SMB_IOCTL_FILENAME);
420         } else {
421             char curdir[256]="";
422
423             GetCurrentDirectory(sizeof(curdir), curdir);
424             if ( curdir[1] == ':' ) {
425                 tbuffer[0] = curdir[0];
426                 tbuffer[1] = ':';
427                 strcpy(tbuffer + 2, SMB_IOCTL_FILENAME);
428             } else if (curdir[0] == curdir[1] &&
429                        (curdir[0] == '\\' || curdir[0] == '/')) 
430             {
431                 int count = 0, i = 0;
432
433                 while (count < 4 && curdir[i]) {
434                     tbuffer[i] = curdir[i];
435                     if ( tbuffer[i] == '\\' ||
436                          tbuffer[i] == '/')
437                         count++;
438                     i++;
439                 }
440                 if (tbuffer[i] == 0)
441                     tbuffer[i++] = '\\';
442                 tbuffer[i] = 0;
443                 strcat(tbuffer, SMB_IOCTL_FILENAME_NOSLASH);
444             }
445         }
446     }
447     if (!tbuffer[0]) {
448         /* No file name starting with drive colon specified, use UNC name */
449         lana_GetNetbiosName(netbiosName,LANA_NETBIOS_NAME_FULL);
450         sprintf(tbuffer,"\\\\%s\\all%s",netbiosName,SMB_IOCTL_FILENAME);
451     }
452
453     fflush(stdout);
454     /* now open the file */
455     fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
456                     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
457                     FILE_FLAG_WRITE_THROUGH, NULL);
458
459         fflush(stdout);
460
461     if (fh == INVALID_HANDLE_VALUE) {
462         int  gonext = 0;
463
464         gle = GetLastError();
465         if (gle && ioctlDebug ) {
466             char buf[4096];
467
468             if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
469                                NULL,
470                                gle,
471                                MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
472                                buf,
473                                4096,
474                                (va_list *) NULL
475                                ) )
476             {
477                 fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
478                         tbuffer,gle,buf);
479             }
480         }
481
482         lana_GetNetbiosName(szClient, LANA_NETBIOS_NAME_FULL);
483
484         if (RegOpenKey (HKEY_CURRENT_USER, 
485                          TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), &hk) == 0)
486         {
487             DWORD dwType = REG_SZ;
488             RegQueryValueEx (hk, TEXT("Logon User Name"), NULL, &dwType, (PBYTE)szUser, &dwSize);
489             RegCloseKey (hk);
490         }
491
492         if ( szUser[0] ) {
493             if ( ioctlDebug )
494                 fprintf(stderr, "pioctl Explorer logon user: [%s]\r\n",szUser);
495
496             sprintf(szPath, "\\\\%s", szClient);
497             memset (&nr, 0x00, sizeof(NETRESOURCE));
498             nr.dwType=RESOURCETYPE_DISK;
499             nr.lpLocalName=0;
500             nr.lpRemoteName=szPath;
501             res = WNetAddConnection2(&nr,NULL,szUser,0);
502             if (res) {
503                 if ( ioctlDebug ) {
504                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
505                              szPath,szUser,res);
506                 }
507                 gonext = 1;
508             }
509
510             sprintf(szPath, "\\\\%s\\all", szClient);
511             res = WNetAddConnection2(&nr,NULL,szUser,0);
512             if (res) {
513                 if ( ioctlDebug ) {
514                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
515                              szPath,szUser,res);
516                 }
517                 gonext = 1;
518             }
519
520             if (gonext)
521                 goto try_lsa_principal;
522
523             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
524                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
525                              FILE_FLAG_WRITE_THROUGH, NULL);
526             fflush(stdout);
527             if (fh == INVALID_HANDLE_VALUE) {
528                 gle = GetLastError();
529                 if (gle && ioctlDebug ) {
530                     char buf[4096];
531
532                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
533                                         NULL,
534                                         gle,
535                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
536                                         buf,
537                                         4096,
538                                         (va_list *) NULL
539                                         ) )
540                     {
541                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
542                                  tbuffer,gle,buf);
543                     }
544                 }
545             }
546         }
547     }
548
549   try_lsa_principal:
550     if (fh == INVALID_HANDLE_VALUE) {
551         int  gonext = 0;
552
553         dwSize = sizeof(szUser);
554         if (GetLSAPrincipalName(szUser, &dwSize)) {
555             if ( ioctlDebug )
556                 fprintf(stderr, "pioctl LSA Principal logon user: [%s]\r\n",szUser);
557
558             sprintf(szPath, "\\\\%s", szClient);
559             memset (&nr, 0x00, sizeof(NETRESOURCE));
560             nr.dwType=RESOURCETYPE_DISK;
561             nr.lpLocalName=0;
562             nr.lpRemoteName=szPath;
563             res = WNetAddConnection2(&nr,NULL,szUser,0);
564             if (res) {
565                 if ( ioctlDebug ) {
566                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
567                              szPath,szUser,res);
568                 }
569                 gonext = 1;
570             }
571
572             sprintf(szPath, "\\\\%s\\all", szClient);
573             res = WNetAddConnection2(&nr,NULL,szUser,0);
574             if (res) {
575                 if ( ioctlDebug ) {
576                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
577                              szPath,szUser,res);
578                 }
579                 gonext = 1;
580             }
581
582             if (gonext)
583                 goto try_sam_compat;
584
585             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
586                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
587                              FILE_FLAG_WRITE_THROUGH, NULL);
588             fflush(stdout);
589             if (fh == INVALID_HANDLE_VALUE) {
590                 gle = GetLastError();
591                 if (gle && ioctlDebug ) {
592                     char buf[4096];
593
594                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
595                                         NULL,
596                                         gle,
597                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
598                                         buf,
599                                         4096,
600                                         (va_list *) NULL
601                                         ) )
602                     {
603                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
604                                  tbuffer,gle,buf);
605                     }
606                 }
607             }
608         }
609     }
610
611   try_sam_compat:
612     if ( fh == INVALID_HANDLE_VALUE ) {
613         dwSize = sizeof(szUser);
614         if (GetUserNameEx(NameSamCompatible, szUser, &dwSize)) {
615             if ( ioctlDebug )
616                 fprintf(stderr, "pioctl SamCompatible logon user: [%s]\r\n",szUser);
617
618             sprintf(szPath, "\\\\%s", szClient);
619             memset (&nr, 0x00, sizeof(NETRESOURCE));
620             nr.dwType=RESOURCETYPE_DISK;
621             nr.lpLocalName=0;
622             nr.lpRemoteName=szPath;
623             res = WNetAddConnection2(&nr,NULL,szUser,0);
624             if (res) {
625                 if ( ioctlDebug ) {
626                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
627                              szPath,szUser,res);
628                 }
629             }
630
631             sprintf(szPath, "\\\\%s\\all", szClient);
632             res = WNetAddConnection2(&nr,NULL,szUser,0);
633             if (res) {
634                 if ( ioctlDebug ) {
635                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
636                              szPath,szUser,res);
637                 }
638                 return -1;
639             }
640
641             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
642                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
643                              FILE_FLAG_WRITE_THROUGH, NULL);
644             fflush(stdout);
645             if (fh == INVALID_HANDLE_VALUE) {
646                 gle = GetLastError();
647                 if (gle && ioctlDebug ) {
648                     char buf[4096];
649
650                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
651                                         NULL,
652                                         gle,
653                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
654                                         buf,
655                                         4096,
656                                         (va_list *) NULL
657                                         ) )
658                     {
659                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
660                                  tbuffer,gle,buf);
661                     }
662                 }
663                 return -1;
664             }
665         } else {
666             fprintf(stderr, "GetUserNameEx(NameSamCompatible) failed: 0x%X\r\n", GetLastError());
667             return -1;
668         }
669     }
670
671     /* return fh and success code */
672     *handlep = fh;
673     return 0;
674 }
675
676 static long
677 Transceive(HANDLE handle, fs_ioctlRequest_t * reqp)
678 {
679     long rcount;
680     long ioCount;
681     DWORD gle;
682
683     rcount = (long)(reqp->mp - reqp->data);
684     if (rcount <= 0) {
685         if ( IoctlDebug() )
686             fprintf(stderr, "pioctl Transceive rcount <= 0: %d\r\n",rcount);
687         return EINVAL;          /* not supposed to happen */
688     }
689
690     if (!WriteFile(handle, reqp->data, rcount, &ioCount, NULL)) {
691         /* failed to write */
692         gle = GetLastError();
693
694         if ( IoctlDebug() )
695             fprintf(stderr, "pioctl Transceive WriteFile failed: 0x%X\r\n",gle);
696         return gle;
697     }
698
699     if (!ReadFile(handle, reqp->data, sizeof(reqp->data), &ioCount, NULL)) {
700         /* failed to read */
701         gle = GetLastError();
702
703         if ( IoctlDebug() )
704             fprintf(stderr, "pioctl Transceive ReadFile failed: 0x%X\r\n",gle);
705         return gle;
706     }
707
708     reqp->nbytes = ioCount;     /* set # of bytes available */
709     reqp->mp = reqp->data;      /* restart marshalling */
710
711     /* return success */
712     return 0;
713 }
714
715 static long
716 MarshallLong(fs_ioctlRequest_t * reqp, long val)
717 {
718     memcpy(reqp->mp, &val, 4);
719     reqp->mp += 4;
720     return 0;
721 }
722
723 static long
724 UnmarshallLong(fs_ioctlRequest_t * reqp, long *valp)
725 {
726     /* not enough data left */
727     if (reqp->nbytes < 4) {
728         if ( IoctlDebug() )
729             fprintf(stderr, "pioctl UnmarshallLong reqp->nbytes < 4: %d\r\n",
730                      reqp->nbytes);
731         return -1;
732     }
733
734     memcpy(valp, reqp->mp, 4);
735     reqp->mp += 4;
736     reqp->nbytes -= 4;
737     return 0;
738 }
739
740 /* includes marshalling NULL pointer as a null (0 length) string */
741 static long
742 MarshallString(fs_ioctlRequest_t * reqp, char *stringp)
743 {
744     int count;
745
746     if (stringp)
747         count = (int)strlen(stringp) + 1;/* space required including null */
748     else
749         count = 1;
750
751     /* watch for buffer overflow */
752     if ((reqp->mp - reqp->data) + count > sizeof(reqp->data)) {
753         if ( IoctlDebug() )
754             fprintf(stderr, "pioctl MarshallString buffer overflow\r\n");
755         return -1;
756     }
757
758     if (stringp)
759         memcpy(reqp->mp, stringp, count);
760     else
761         *(reqp->mp) = 0;
762     reqp->mp += count;
763     return 0;
764 }
765
766 /* take a path with a drive letter, possibly relative, and return a full path
767  * without the drive letter.  This is the full path relative to the working
768  * dir for that drive letter.  The input and output paths can be the same.
769  */
770 static long
771 fs_GetFullPath(char *pathp, char *outPathp, long outSize)
772 {
773     char tpath[1000];
774     char origPath[1000];
775     char *firstp;
776     long code;
777     int pathHasDrive;
778     int doSwitch;
779     char newPath[3];
780     char * p;
781
782     if (pathp[0] != 0 && pathp[1] == ':') {
783         /* there's a drive letter there */
784         firstp = pathp + 2;
785         pathHasDrive = 1;
786     } else {
787         firstp = pathp;
788         pathHasDrive = 0;
789     }
790
791     if ( firstp[0] == '\\' && firstp[1] == '\\' || 
792          firstp[0] == '/' && firstp[1] == '/') {
793         /* UNC path - strip off the server and sharename */
794         int i, count;
795         for ( i=2,count=2; count < 4 && firstp[i]; i++ ) {
796             if ( firstp[i] == '\\' || firstp[i] == '/' ) {
797                 count++;
798             }
799         }
800         if ( firstp[i] == 0 ) {
801             strcpy(outPathp,"\\");
802         } else {
803             strcpy(outPathp,&firstp[--i]);
804         }
805         for (p=outPathp ;*p; p++) {
806             if (*p == '/')
807                 *p = '\\';
808         }
809         return 0;
810     } else if (firstp[0] == '\\' || firstp[0] == '/') {
811         /* already an absolute pathname, just copy it back */
812         strcpy(outPathp, firstp);
813         for (p=outPathp ;*p; p++) {
814             if (*p == '/')
815                 *p = '\\';
816         }
817         return 0;
818     }
819
820     GetCurrentDirectory(sizeof(origPath), origPath);
821
822     doSwitch = 0;
823     if (pathHasDrive && (*pathp & ~0x20) != (origPath[0] & ~0x20)) {
824         /* a drive has been specified and it isn't our current drive.
825          * to get path, switch to it first.  Must case-fold drive letters
826          * for user convenience.
827          */
828         doSwitch = 1;
829         newPath[0] = *pathp;
830         newPath[1] = ':';
831         newPath[2] = 0;
832         if (!SetCurrentDirectory(newPath)) {
833             code = GetLastError();
834
835             if ( IoctlDebug() )
836                 fprintf(stderr, "pioctl fs_GetFullPath SetCurrentDirectory(%s) failed: 0x%X\r\n",
837                          newPath, code);
838             return code;
839         }
840     }
841
842     /* now get the absolute path to the current wdir in this drive */
843     GetCurrentDirectory(sizeof(tpath), tpath);
844     if (tpath[1] == ':')
845         strcpy(outPathp, tpath + 2);    /* skip drive letter */
846     else if ( tpath[0] == '\\' && tpath[1] == '\\'||
847               tpath[0] == '/' && tpath[1] == '/') {
848         /* UNC path - strip off the server and sharename */
849         int i, count;
850         for ( i=2,count=2; count < 4 && tpath[i]; i++ ) {
851             if ( tpath[i] == '\\' || tpath[i] == '/' ) {
852                 count++;
853             }
854         }
855         if ( tpath[i] == 0 ) {
856             strcpy(outPathp,"\\");
857         } else {
858             strcpy(outPathp,&tpath[--i]);
859         }
860     } else {
861         /* this should never happen */
862         strcpy(outPathp, tpath);
863     }
864
865     /* if there is a non-null name after the drive, append it */
866     if (*firstp != 0) {
867         int len = (int)strlen(outPathp);
868         if (outPathp[len-1] != '\\' && outPathp[len-1] != '/') 
869             strcat(outPathp, "\\");
870         strcat(outPathp, firstp);
871     }
872
873     /* finally, if necessary, switch back to our home drive letter */
874     if (doSwitch) {
875         SetCurrentDirectory(origPath);
876     }
877
878     for (p=outPathp ;*p; p++) {
879         if (*p == '/')
880             *p = '\\';
881     }
882     return 0;
883 }
884
885 long
886 pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow)
887 {
888     fs_ioctlRequest_t preq;
889     long code;
890     long temp;
891     char fullPath[1000];
892     HANDLE reqHandle;
893
894     code = GetIoctlHandle(pathp, &reqHandle);
895     if (code) {
896         if (pathp)
897             errno = EINVAL;
898         else
899             errno = ENODEV;
900         return code;
901     }
902
903     /* init the request structure */
904     InitFSRequest(&preq);
905
906     /* marshall the opcode, the path name and the input parameters */
907     MarshallLong(&preq, opcode);
908     /* when marshalling the path, remove the drive letter, since we already
909      * used the drive letter to find the AFS daemon; we don't need it any more.
910      * Eventually we'll expand relative path names here, too, since again, only
911      * we understand those.
912      */
913     if (pathp) {
914         code = fs_GetFullPath(pathp, fullPath, sizeof(fullPath));
915         if (code) {
916             CloseHandle(reqHandle);
917             errno = EINVAL;
918             return code;
919         }
920     } else {
921         strcpy(fullPath, "");
922     }
923
924     MarshallString(&preq, fullPath);
925     if (blobp->in_size) {
926         if (blobp->in_size > sizeof(preq.data) - (preq.mp - preq.data)*sizeof(char)) {
927             errno = E2BIG;
928             return -1;
929         }
930         memcpy(preq.mp, blobp->in, blobp->in_size);
931         preq.mp += blobp->in_size;
932     }
933
934     /* now make the call */
935     code = Transceive(reqHandle, &preq);
936     if (code) {
937         CloseHandle(reqHandle);
938         return code;
939     }
940
941     /* now unmarshall the return value */
942     if (UnmarshallLong(&preq, &temp) != 0) {
943         CloseHandle(reqHandle);
944         return -1;
945     }
946
947     if (temp != 0) {
948         CloseHandle(reqHandle);
949         errno = CMtoUNIXerror(temp);
950         if ( IoctlDebug() )
951             fprintf(stderr, "pioctl temp != 0: 0x%X\r\n",temp);
952         return -1;
953     }
954
955     /* otherwise, unmarshall the output parameters */
956     if (blobp->out_size) {
957         temp = blobp->out_size;
958         if (preq.nbytes < temp)
959             temp = preq.nbytes;
960         memcpy(blobp->out, preq.mp, temp);
961         blobp->out_size = temp;
962     }
963
964     /* and return success */
965     CloseHandle(reqHandle);
966     return 0;
967 }