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