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