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