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