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