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