windows-universal-error-handling-20051015
[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
260 #define KERB5DLL "krb5_32.dll"
261 static BOOL
262 IsKrb5Available()
263 {
264     static HINSTANCE hKrb5DLL = 0;
265
266     if ( hKrb5DLL )
267         return TRUE;
268
269     hKrb5DLL = LoadLibrary(KERB5DLL);
270     if (hKrb5DLL) {
271         if (!LoadFuncs(KERB5DLL, krb5_fi, 0, 0, 1, 0, 0))
272         {
273             FreeLibrary(hKrb5DLL);
274             hKrb5DLL = 0;
275             return FALSE;
276         }
277         return TRUE;
278     }
279     return FALSE;
280 }
281
282 static BOOL
283 GetLSAPrincipalName(char * szUser, DWORD *dwSize)
284 {
285     krb5_context   ctx = 0;
286     krb5_error_code code;
287     krb5_ccache mslsa_ccache=0;
288     krb5_principal princ = 0;
289     char * pname = 0;
290     BOOL success = 0;
291
292     if (!IsKrb5Available())
293         return FALSE;
294
295     if (code = pkrb5_init_context(&ctx))
296         goto cleanup;
297
298     if (code = pkrb5_cc_resolve(ctx, "MSLSA:", &mslsa_ccache))
299         goto cleanup;
300
301     if (code = pkrb5_cc_get_principal(ctx, mslsa_ccache, &princ))
302         goto cleanup;
303
304     if (code = pkrb5_unparse_name(ctx, princ, &pname))
305         goto cleanup;
306
307     if ( strlen(pname) < *dwSize ) {
308         strncpy(szUser, pname, *dwSize);
309         szUser[*dwSize-1] = '\0';
310         success = 1;
311     }
312     *dwSize = strlen(pname);
313
314   cleanup:
315     if (pname)
316         pkrb5_free_unparsed_name(ctx, pname);
317
318     if (princ)
319         pkrb5_free_principal(ctx, princ);
320
321     if (mslsa_ccache)
322         pkrb5_cc_close(ctx, mslsa_ccache);
323
324     if (ctx)
325         pkrb5_free_context(ctx);
326     return success;
327 }
328
329 static long
330 GetIoctlHandle(char *fileNamep, HANDLE * handlep)
331 {
332     char *drivep;
333     char netbiosName[MAX_NB_NAME_LENGTH];
334     char tbuffer[256]="";
335     HANDLE fh;
336     HKEY hk;
337     char szUser[128] = "";
338     char szClient[MAX_PATH] = "";
339     char szPath[MAX_PATH] = "";
340     NETRESOURCE nr;
341     DWORD res;
342     DWORD ioctlDebug = IoctlDebug();
343     DWORD gle;
344     DWORD dwSize = sizeof(szUser);
345
346 #ifndef AFSIFS
347     if (fileNamep) {
348         drivep = strchr(fileNamep, ':');
349         if (drivep && (drivep - fileNamep) >= 1) {
350             tbuffer[0] = *(drivep - 1);
351             tbuffer[1] = ':';
352             strcpy(tbuffer + 2, SMB_IOCTL_FILENAME);
353         } else if (fileNamep[0] == fileNamep[1] && 
354                    (fileNamep[0] == '\\' || fileNamep[0] == '/'))
355         {
356             int count = 0, i = 0;
357
358             while (count < 4 && fileNamep[i]) {
359                 tbuffer[i] = fileNamep[i];
360                 if ( tbuffer[i] == '\\' ||
361                      tbuffer[i] == '/')
362                     count++;
363                 i++;
364             }
365             if (fileNamep[i] == 0)
366                 tbuffer[i++] = '\\';
367             tbuffer[i] = 0;
368             strcat(tbuffer, SMB_IOCTL_FILENAME);
369         } else {
370             char curdir[256]="";
371
372             GetCurrentDirectory(sizeof(curdir), curdir);
373             if ( curdir[1] == ':' ) {
374                 tbuffer[0] = curdir[0];
375                 tbuffer[1] = ':';
376                 strcpy(tbuffer + 2, SMB_IOCTL_FILENAME);
377             } else if (curdir[0] == curdir[1] &&
378                        (curdir[0] == '\\' || curdir[0] == '/')) 
379             {
380                 int count = 0, i = 0;
381
382                 while (count < 4 && curdir[i]) {
383                     tbuffer[i] = curdir[i];
384                     if ( tbuffer[i] == '\\' ||
385                          tbuffer[i] == '/')
386                         count++;
387                     i++;
388                 }
389                 if (tbuffer[i] == 0)
390                     tbuffer[i++] = '\\';
391                 tbuffer[i] = 0;
392                 strcat(tbuffer, SMB_IOCTL_FILENAME_NOSLASH);
393             }
394         }
395     }
396     if (!tbuffer[0]) {
397         /* No file name starting with drive colon specified, use UNC name */
398         lana_GetNetbiosName(netbiosName,LANA_NETBIOS_NAME_FULL);
399         sprintf(tbuffer,"\\\\%s\\all%s",netbiosName,SMB_IOCTL_FILENAME);
400     }
401 #else   
402     sprintf(tbuffer,"\\\\.\\afscom\\ioctl");
403 #endif 
404
405     fflush(stdout);
406     /* now open the file */
407     fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
408                     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
409                     FILE_FLAG_WRITE_THROUGH, NULL);
410
411         fflush(stdout);
412
413 #ifdef AFSIFS
414     if (fh == INVALID_HANDLE_VALUE) {
415         return -1;
416     }
417 #endif
418         
419         if (fh == INVALID_HANDLE_VALUE) {
420         int  gonext = 0;
421
422         gle = GetLastError();
423         if (gle && ioctlDebug ) {
424             char buf[4096];
425
426             if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
427                                NULL,
428                                gle,
429                                MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
430                                buf,
431                                4096,
432                                (va_list *) NULL
433                                ) )
434             {
435                 fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
436                         tbuffer,gle,buf);
437             }
438         }
439
440         lana_GetNetbiosName(szClient, LANA_NETBIOS_NAME_FULL);
441
442         if (RegOpenKey (HKEY_CURRENT_USER, 
443                          TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), &hk) == 0)
444         {
445             DWORD dwType = REG_SZ;
446             RegQueryValueEx (hk, TEXT("Logon User Name"), NULL, &dwType, (PBYTE)szUser, &dwSize);
447             RegCloseKey (hk);
448         }
449
450         if ( szUser[0] ) {
451             if ( ioctlDebug )
452                 fprintf(stderr, "pioctl Explorer logon user: [%s]\r\n",szUser);
453
454             sprintf(szPath, "\\\\%s", szClient);
455             memset (&nr, 0x00, sizeof(NETRESOURCE));
456             nr.dwType=RESOURCETYPE_DISK;
457             nr.lpLocalName=0;
458             nr.lpRemoteName=szPath;
459             res = WNetAddConnection2(&nr,NULL,szUser,0);
460             if (res) {
461                 if ( ioctlDebug ) {
462                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
463                              szPath,szUser,res);
464                 }
465                 gonext = 1;
466             }
467
468             sprintf(szPath, "\\\\%s\\all", szClient);
469             res = WNetAddConnection2(&nr,NULL,szUser,0);
470             if (res) {
471                 if ( ioctlDebug ) {
472                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
473                              szPath,szUser,res);
474                 }
475                 gonext = 1;
476             }
477
478             if (gonext)
479                 goto try_lsa_principal;
480
481             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
482                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
483                              FILE_FLAG_WRITE_THROUGH, NULL);
484             fflush(stdout);
485             if (fh == INVALID_HANDLE_VALUE) {
486                 gle = GetLastError();
487                 if (gle && ioctlDebug ) {
488                     char buf[4096];
489
490                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
491                                         NULL,
492                                         gle,
493                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
494                                         buf,
495                                         4096,
496                                         (va_list *) NULL
497                                         ) )
498                     {
499                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
500                                  tbuffer,gle,buf);
501                     }
502                 }
503             }
504         }
505     }
506
507   try_lsa_principal:
508     if (fh == INVALID_HANDLE_VALUE) {
509         int  gonext = 0;
510
511         dwSize = sizeof(szUser);
512         if (GetLSAPrincipalName(szUser, &dwSize)) {
513             if ( ioctlDebug )
514                 fprintf(stderr, "pioctl LSA Principal logon user: [%s]\r\n",szUser);
515
516             sprintf(szPath, "\\\\%s", szClient);
517             memset (&nr, 0x00, sizeof(NETRESOURCE));
518             nr.dwType=RESOURCETYPE_DISK;
519             nr.lpLocalName=0;
520             nr.lpRemoteName=szPath;
521             res = WNetAddConnection2(&nr,NULL,szUser,0);
522             if (res) {
523                 if ( ioctlDebug ) {
524                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
525                              szPath,szUser,res);
526                 }
527                 gonext = 1;
528             }
529
530             sprintf(szPath, "\\\\%s\\all", szClient);
531             res = WNetAddConnection2(&nr,NULL,szUser,0);
532             if (res) {
533                 if ( ioctlDebug ) {
534                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
535                              szPath,szUser,res);
536                 }
537                 gonext = 1;
538             }
539
540             if (gonext)
541                 goto try_sam_compat;
542
543             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
544                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
545                              FILE_FLAG_WRITE_THROUGH, NULL);
546             fflush(stdout);
547             if (fh == INVALID_HANDLE_VALUE) {
548                 gle = GetLastError();
549                 if (gle && ioctlDebug ) {
550                     char buf[4096];
551
552                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
553                                         NULL,
554                                         gle,
555                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
556                                         buf,
557                                         4096,
558                                         (va_list *) NULL
559                                         ) )
560                     {
561                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
562                                  tbuffer,gle,buf);
563                     }
564                 }
565             }
566         }
567     }
568
569   try_sam_compat:
570     if ( fh == INVALID_HANDLE_VALUE ) {
571         dwSize = sizeof(szUser);
572         if (GetUserNameEx(NameSamCompatible, szUser, &dwSize)) {
573             if ( ioctlDebug )
574                 fprintf(stderr, "pioctl SamCompatible logon user: [%s]\r\n",szUser);
575
576             sprintf(szPath, "\\\\%s", szClient);
577             memset (&nr, 0x00, sizeof(NETRESOURCE));
578             nr.dwType=RESOURCETYPE_DISK;
579             nr.lpLocalName=0;
580             nr.lpRemoteName=szPath;
581             res = WNetAddConnection2(&nr,NULL,szUser,0);
582             if (res) {
583                 if ( ioctlDebug ) {
584                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
585                              szPath,szUser,res);
586                 }
587             }
588
589             sprintf(szPath, "\\\\%s\\all", szClient);
590             res = WNetAddConnection2(&nr,NULL,szUser,0);
591             if (res) {
592                 if ( ioctlDebug ) {
593                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
594                              szPath,szUser,res);
595                 }
596                 return -1;
597             }
598
599             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
600                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
601                              FILE_FLAG_WRITE_THROUGH, NULL);
602             fflush(stdout);
603             if (fh == INVALID_HANDLE_VALUE) {
604                 gle = GetLastError();
605                 if (gle && ioctlDebug ) {
606                     char buf[4096];
607
608                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
609                                         NULL,
610                                         gle,
611                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
612                                         buf,
613                                         4096,
614                                         (va_list *) NULL
615                                         ) )
616                     {
617                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%X\r\n\t[%s]\r\n",
618                                  tbuffer,gle,buf);
619                     }
620                 }
621                 return -1;
622             }
623         } else {
624             fprintf(stderr, "GetUserNameEx(NameSamCompatible) failed: 0x%X\r\n", GetLastError());
625             return -1;
626         }
627     }
628
629     /* return fh and success code */
630     *handlep = fh;
631     return 0;
632 }
633
634 static long
635 Transceive(HANDLE handle, fs_ioctlRequest_t * reqp)
636 {
637     long rcount;
638     long ioCount;
639     DWORD gle;
640         char *data;
641
642     rcount = reqp->mp - reqp->data;
643     if (rcount <= 0) {
644         if ( IoctlDebug() )
645             fprintf(stderr, "pioctl Transceive rcount <= 0: %d\r\n",rcount);
646         return EINVAL;          /* not supposed to happen */
647     }
648
649 #ifndef AFSIFS
650     if (!WriteFile(handle, reqp->data, rcount, &ioCount, NULL)) {
651         /* failed to write */
652         gle = GetLastError();
653
654         if ( IoctlDebug() )
655             fprintf(stderr, "pioctl Transceive WriteFile failed: 0x%X\r\n",gle);
656         return gle;
657     }
658
659     if (!ReadFile(handle, reqp->data, sizeof(reqp->data), &ioCount, NULL)) {
660         /* failed to read */
661         gle = GetLastError();
662
663         if ( IoctlDebug() )
664             fprintf(stderr, "pioctl Transceive ReadFile failed: 0x%X\r\n",gle);
665         return gle;
666     }
667 #else
668     /* ioctl completes as one operation, so copy input to a new buffer, and use as output buffer */
669     data = malloc(rcount);
670     memcpy(data, reqp->data, rcount);
671     if (!DeviceIoControl(handle, IOCTL_AFSRDR_IOCTL, data, rcount, reqp->data, sizeof(reqp->data), &ioCount, NULL))
672     {
673         free(data);
674         return GetLastError();
675     }
676     free(data);
677 #endif
678
679     reqp->nbytes = ioCount;     /* set # of bytes available */
680     reqp->mp = reqp->data;      /* restart marshalling */
681
682     /* return success */
683     return 0;
684 }
685
686 static long
687 MarshallLong(fs_ioctlRequest_t * reqp, long val)
688 {
689     memcpy(reqp->mp, &val, 4);
690     reqp->mp += 4;
691     return 0;
692 }
693
694 static long
695 UnmarshallLong(fs_ioctlRequest_t * reqp, long *valp)
696 {
697     /* not enough data left */
698     if (reqp->nbytes < 4) {
699         if ( IoctlDebug() )
700             fprintf(stderr, "pioctl UnmarshallLong reqp->nbytes < 4: %d\r\n",
701                      reqp->nbytes);
702         return -1;
703     }
704
705     memcpy(valp, reqp->mp, 4);
706     reqp->mp += 4;
707     reqp->nbytes -= 4;
708     return 0;
709 }
710
711 /* includes marshalling NULL pointer as a null (0 length) string */
712 static long
713 MarshallString(fs_ioctlRequest_t * reqp, char *stringp)
714 {
715     int count;
716
717     if (stringp)
718         count = strlen(stringp) + 1;    /* space required including null */
719     else
720         count = 1;
721
722     /* watch for buffer overflow */
723     if ((reqp->mp - reqp->data) + count > sizeof(reqp->data)) {
724         if ( IoctlDebug() )
725             fprintf(stderr, "pioctl MarshallString buffer overflow\r\n");
726         return -1;
727     }
728
729     if (stringp)
730         memcpy(reqp->mp, stringp, count);
731     else
732         *(reqp->mp) = 0;
733     reqp->mp += count;
734     return 0;
735 }
736
737 /* take a path with a drive letter, possibly relative, and return a full path
738  * without the drive letter.  This is the full path relative to the working
739  * dir for that drive letter.  The input and output paths can be the same.
740  */
741 static long
742 fs_GetFullPath(char *pathp, char *outPathp, long outSize)
743 {
744     char tpath[1000];
745     char origPath[1000];
746     char *firstp;
747     long code;
748     int pathHasDrive;
749     int doSwitch;
750     char newPath[3];
751     HANDLE rootDir;
752     wchar_t *wpath;
753     unsigned long length;
754     char * p;
755
756 #ifdef AFSIFS
757     if (!pathp)
758         return CM_ERROR_NOSUCHPATH;
759
760     //sprintf(tpath, "%c:\\", pathp[0]);
761     rootDir = CreateFile(pathp, 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
762     if (rootDir == INVALID_HANDLE_VALUE)
763         return CM_ERROR_NOSUCHPATH;
764
765     wpath = tpath;
766     length = 0;
767     if (!DeviceIoControl(rootDir, IOCTL_AFSRDR_GET_PATH, NULL, 0, wpath, 1000, &length, NULL))
768     {
769         CloseHandle(rootDir);
770         return CM_ERROR_NOSUCHPATH;
771     }
772     CloseHandle(rootDir);
773
774     code = WideCharToMultiByte(CP_UTF8, 0/*WC_NO_BEST_FIT_CHARS*/, wpath, length/sizeof(wchar_t), outPathp, outSize/sizeof(wchar_t), NULL, NULL);
775
776 //    strcpy(outPathp, tpath);
777     return 0;
778 #endif
779
780     if (pathp[0] != 0 && pathp[1] == ':') {
781         /* there's a drive letter there */
782         firstp = pathp + 2;
783         pathHasDrive = 1;
784     } else {
785         firstp = pathp;
786         pathHasDrive = 0;
787     }
788
789     if ( firstp[0] == '\\' && firstp[1] == '\\' || 
790          firstp[0] == '/' && firstp[1] == '/') {
791         /* UNC path - strip off the server and sharename */
792         int i, count;
793         for ( i=2,count=2; count < 4 && firstp[i]; i++ ) {
794             if ( firstp[i] == '\\' || firstp[i] == '/' ) {
795                 count++;
796             }
797         }
798         if ( firstp[i] == 0 ) {
799             strcpy(outPathp,"\\");
800         } else {
801             strcpy(outPathp,&firstp[--i]);
802         }
803         for (p=outPathp ;*p; p++) {
804             if (*p == '/')
805                 *p = '\\';
806         }
807         return 0;
808     } else if (firstp[0] == '\\' || firstp[0] == '/') {
809         /* already an absolute pathname, just copy it back */
810         strcpy(outPathp, firstp);
811         for (p=outPathp ;*p; p++) {
812             if (*p == '/')
813                 *p = '\\';
814         }
815         return 0;
816     }
817
818     GetCurrentDirectory(sizeof(origPath), origPath);
819
820     doSwitch = 0;
821     if (pathHasDrive && (*pathp & ~0x20) != (origPath[0] & ~0x20)) {
822         /* a drive has been specified and it isn't our current drive.
823          * to get path, switch to it first.  Must case-fold drive letters
824          * for user convenience.
825          */
826         doSwitch = 1;
827         newPath[0] = *pathp;
828         newPath[1] = ':';
829         newPath[2] = 0;
830         if (!SetCurrentDirectory(newPath)) {
831             code = GetLastError();
832
833             if ( IoctlDebug() )
834                 fprintf(stderr, "pioctl fs_GetFullPath SetCurrentDirectory(%s) failed: 0x%X\r\n",
835                          newPath, code);
836             return code;
837         }
838     }
839
840     /* now get the absolute path to the current wdir in this drive */
841     GetCurrentDirectory(sizeof(tpath), tpath);
842     if (tpath[1] == ':')
843         strcpy(outPathp, tpath + 2);    /* skip drive letter */
844     else if ( tpath[0] == '\\' && tpath[1] == '\\'||
845               tpath[0] == '/' && tpath[1] == '/') {
846         /* UNC path - strip off the server and sharename */
847         int i, count;
848         for ( i=2,count=2; count < 4 && tpath[i]; i++ ) {
849             if ( tpath[i] == '\\' || tpath[i] == '/' ) {
850                 count++;
851             }
852         }
853         if ( tpath[i] == 0 ) {
854             strcpy(outPathp,"\\");
855         } else {
856             strcpy(outPathp,&tpath[--i]);
857         }
858     } else {
859         /* this should never happen */
860         strcpy(outPathp, tpath);
861     }
862
863     /* if there is a non-null name after the drive, append it */
864     if (*firstp != 0) {
865         int len = strlen(outPathp);
866         if (outPathp[len-1] != '\\' && outPathp[len-1] != '/') 
867             strcat(outPathp, "\\");
868         strcat(outPathp, firstp);
869     }
870
871     /* finally, if necessary, switch back to our home drive letter */
872     if (doSwitch) {
873         SetCurrentDirectory(origPath);
874     }
875
876     for (p=outPathp ;*p; p++) {
877         if (*p == '/')
878             *p = '\\';
879     }
880     return 0;
881 }
882
883 long
884 pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow)
885 {
886     fs_ioctlRequest_t preq;
887     long code;
888     long temp;
889     char fullPath[1000];
890     HANDLE reqHandle;
891
892     code = GetIoctlHandle(pathp, &reqHandle);
893     if (code) {
894         if (pathp)
895             errno = EINVAL;
896         else
897             errno = ENODEV;
898         return code;
899     }
900
901     /* init the request structure */
902     InitFSRequest(&preq);
903
904     /* marshall the opcode, the path name and the input parameters */
905     MarshallLong(&preq, opcode);
906     /* when marshalling the path, remove the drive letter, since we already
907      * used the drive letter to find the AFS daemon; we don't need it any more.
908      * Eventually we'll expand relative path names here, too, since again, only
909      * we understand those.
910      */
911     if (pathp) {
912         code = fs_GetFullPath(pathp, fullPath, sizeof(fullPath));
913         if (code) {
914             CloseHandle(reqHandle);
915             errno = EINVAL;
916             return code;
917         }
918     } else {
919         strcpy(fullPath, "");
920     }
921
922     MarshallString(&preq, fullPath);
923     if (blobp->in_size) {
924         memcpy(preq.mp, blobp->in, blobp->in_size);
925         preq.mp += blobp->in_size;
926     }
927
928     /* now make the call */
929     code = Transceive(reqHandle, &preq);
930     if (code) {
931         CloseHandle(reqHandle);
932         return code;
933     }
934
935     /* now unmarshall the return value */
936     if (UnmarshallLong(&preq, &temp) != 0) {
937         CloseHandle(reqHandle);
938         return -1;
939     }
940
941     if (temp != 0) {
942         CloseHandle(reqHandle);
943         errno = CMtoUNIXerror(temp);
944         if ( IoctlDebug() )
945             fprintf(stderr, "pioctl temp != 0: 0x%X\r\n",temp);
946         return -1;
947     }
948
949     /* otherwise, unmarshall the output parameters */
950     if (blobp->out_size) {
951         temp = blobp->out_size;
952         if (preq.nbytes < temp)
953             temp = preq.nbytes;
954         memcpy(blobp->out, preq.mp, temp);
955         blobp->out_size = temp;
956     }
957
958     /* and return success */
959     CloseHandle(reqHandle);
960     return 0;
961 }