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