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