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