windows-pioctl-20041204
[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
44 #include <lanahelper.h>
45
46 static char AFSConfigKeyName[] =
47     "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters";
48
49 #define FS_IOCTLREQUEST_MAXSIZE 8192
50 /* big structure for representing and storing an IOCTL request */
51 typedef struct fs_ioctlRequest {
52     char *mp;                   /* marshalling/unmarshalling ptr */
53     long nbytes;                /* bytes received (when unmarshalling) */
54     char data[FS_IOCTLREQUEST_MAXSIZE]; /* data we're marshalling */
55 } fs_ioctlRequest_t;
56
57 static int
58 CMtoUNIXerror(int cm_code)
59 {
60     switch (cm_code) {
61     case CM_ERROR_TIMEDOUT:
62         return ETIMEDOUT;
63     case CM_ERROR_NOACCESS:
64         return EACCES;
65     case CM_ERROR_NOSUCHFILE:
66         return ENOENT;
67     case CM_ERROR_INVAL:
68         return EINVAL;
69     case CM_ERROR_BADFD:
70         return EBADF;
71     case CM_ERROR_EXISTS:
72         return EEXIST;
73     case CM_ERROR_CROSSDEVLINK:
74         return EXDEV;
75     case CM_ERROR_NOTDIR:
76         return ENOTDIR;
77     case CM_ERROR_ISDIR:
78         return EISDIR;
79     case CM_ERROR_READONLY:
80         return EROFS;
81     case CM_ERROR_WOULDBLOCK:
82         return EWOULDBLOCK;
83     case CM_ERROR_NOSUCHCELL:
84         return ESRCH;           /* hack */
85     case CM_ERROR_NOSUCHVOLUME:
86         return EPIPE;           /* hack */
87     case CM_ERROR_NOMORETOKENS:
88         return EDOM;            /* hack */
89     case CM_ERROR_TOOMANYBUFS:
90         return EFBIG;           /* hack */
91     default:
92         return ENOTTY;
93     }
94 }
95
96 static void
97 InitFSRequest(fs_ioctlRequest_t * rp)
98 {
99     rp->mp = rp->data;
100     rp->nbytes = 0;
101 }
102
103 static BOOL
104 IoctlDebug(void)
105 {
106     static int init = 0;
107     static BOOL debug = 0;
108
109     if ( !init ) {
110         HKEY hk;
111
112         if (RegOpenKey (HKEY_LOCAL_MACHINE, 
113                          TEXT("Software\\OpenAFS\\Client"), &hk) == 0)
114         {
115             DWORD dwSize = sizeof(BOOL);
116             DWORD dwType = REG_DWORD;
117             RegQueryValueEx (hk, TEXT("IoctlDebug"), NULL, &dwType, (PBYTE)&debug, &dwSize);
118             RegCloseKey (hk);
119         }
120
121         init = 1;
122     }
123
124     return debug;
125 }
126
127 static long
128 GetIoctlHandle(char *fileNamep, HANDLE * handlep)
129 {
130     char *drivep;
131     char netbiosName[MAX_NB_NAME_LENGTH];
132     char tbuffer[256]="";
133     HANDLE fh;
134     HKEY hk;
135     char szUser[128] = "";
136     char szClient[MAX_PATH] = "";
137     char szPath[MAX_PATH] = "";
138     NETRESOURCE nr;
139     DWORD res;
140     DWORD ioctlDebug = IoctlDebug();
141     DWORD gle;
142     DWORD dwSize = sizeof(szUser);
143
144     if (fileNamep) {
145         drivep = strchr(fileNamep, ':');
146         if (drivep && (drivep - fileNamep) >= 1) {
147             tbuffer[0] = *(drivep - 1);
148             tbuffer[1] = ':';
149             strcpy(tbuffer + 2, SMB_IOCTL_FILENAME);
150         } else if (fileNamep[0] == fileNamep[1] && 
151                                fileNamep[0] == '\\')
152         {
153             int count = 0, i = 0;
154
155             while (count < 4 && fileNamep[i]) {
156                 tbuffer[i] = fileNamep[i];
157                 if ( tbuffer[i++] == '\\' )
158                     count++;
159             }
160             if (fileNamep[i] == 0)
161                 tbuffer[i++] = '\\';
162             tbuffer[i] = 0;
163             strcat(tbuffer, SMB_IOCTL_FILENAME);
164         } else {
165             char curdir[256]="";
166
167             GetCurrentDirectory(sizeof(curdir), curdir);
168             if ( curdir[1] == ':' ) {
169                 tbuffer[0] = curdir[0];
170                 tbuffer[1] = ':';
171                 strcpy(tbuffer + 2, SMB_IOCTL_FILENAME);
172             } else if (curdir[0] == curdir[1] &&
173                        curdir[0] == '\\') 
174             {
175                 int count = 0, i = 0;
176
177                 while (count < 4 && curdir[i]) {
178                     tbuffer[i] = curdir[i];
179                     if ( tbuffer[i++] == '\\' )
180                         count++;
181                 }
182                 if (tbuffer[i] == 0)
183                     tbuffer[i++] = '\\';
184                 tbuffer[i] = 0;
185                 strcat(tbuffer, SMB_IOCTL_FILENAME_NOSLASH);
186             }
187         }
188     }
189     if (!tbuffer[0]) {
190         /* No file name starting with drive colon specified, use UNC name */
191         lana_GetNetbiosName(netbiosName,LANA_NETBIOS_NAME_FULL);
192         sprintf(tbuffer,"\\\\%s\\all%s",netbiosName,SMB_IOCTL_FILENAME);
193     }
194
195     fflush(stdout);
196     /* now open the file */
197     fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
198                     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
199                     FILE_FLAG_WRITE_THROUGH, NULL);
200     fflush(stdout);
201     if (fh == INVALID_HANDLE_VALUE) {
202         gle = GetLastError();
203         if (gle && ioctlDebug ) {
204             char buf[4096];
205
206             if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
207                                NULL,
208                                gle,
209                                MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
210                                buf,
211                                4096,
212                                (va_list *) NULL
213                                ) )
214             {
215                 fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%8X\r\n\t[%s]\r\n",
216                         tbuffer,gle,buf);
217             }
218         }
219 #ifdef COMMENT
220         if (gle != ERROR_DOWNGRADE_DETECTED)
221             return -1;                                   
222 #endif
223
224         lana_GetNetbiosName(szClient, LANA_NETBIOS_NAME_FULL);
225
226         if (RegOpenKey (HKEY_CURRENT_USER, 
227                          TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), &hk) == 0)
228         {
229             DWORD dwType = REG_SZ;
230             RegQueryValueEx (hk, TEXT("Logon User Name"), NULL, &dwType, (PBYTE)szUser, &dwSize);
231             RegCloseKey (hk);
232         }
233
234         if ( szUser[0] ) {
235             if ( ioctlDebug )
236                 fprintf(stderr, "pioctl logon user: [%s]\r\n",szUser);
237
238             sprintf(szPath, "\\\\%s", szClient);
239             memset (&nr, 0x00, sizeof(NETRESOURCE));
240             nr.dwType=RESOURCETYPE_DISK;
241             nr.lpLocalName=0;
242             nr.lpRemoteName=szPath;
243             res = WNetAddConnection2(&nr,NULL,szUser,0);
244             if (res) {
245                 if ( ioctlDebug ) {
246                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
247                              szPath,szUser,res);
248                 }
249
250                 sprintf(szPath, "\\\\%s\\all", szClient);
251                 res = WNetAddConnection2(&nr,NULL,szUser,0);
252                 if (res) {
253                     if ( ioctlDebug ) {
254                         fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
255                                  szPath,szUser,res);
256                     }
257                     goto next_attempt;
258                 }
259             }
260
261             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
262                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
263                              FILE_FLAG_WRITE_THROUGH, NULL);
264             fflush(stdout);
265             if (fh == INVALID_HANDLE_VALUE) {
266                 gle = GetLastError();
267                 if (gle && ioctlDebug ) {
268                     char buf[4096];
269
270                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
271                                         NULL,
272                                         gle,
273                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
274                                         buf,
275                                         4096,
276                                         (va_list *) NULL
277                                         ) )
278                     {
279                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%8X\r\n\t[%s]\r\n",
280                                  tbuffer,gle,buf);
281                     }
282                 }
283             }
284         }
285     }
286
287   next_attempt:
288     if ( fh == INVALID_HANDLE_VALUE ) {
289         if (GetUserNameEx(NameSamCompatible, szUser, &dwSize)) {
290             if ( ioctlDebug )
291                 fprintf(stderr, "pioctl logon user: [%s]\r\n",szUser);
292
293             sprintf(szPath, "\\\\%s", szClient);
294             memset (&nr, 0x00, sizeof(NETRESOURCE));
295             nr.dwType=RESOURCETYPE_DISK;
296             nr.lpLocalName=0;
297             nr.lpRemoteName=szPath;
298             res = WNetAddConnection2(&nr,NULL,szUser,0);
299             if (res) {
300                 if ( ioctlDebug ) {
301                     fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
302                              szPath,szUser,res);
303                 }
304
305                 sprintf(szPath, "\\\\%s\\all", szClient);
306                 res = WNetAddConnection2(&nr,NULL,szUser,0);
307                 if (res) {
308                     if ( ioctlDebug ) {
309                         fprintf(stderr, "pioctl WNetAddConnection2(%s,%s) failed: 0x%X\r\n",
310                                  szPath,szUser,res);
311                     }
312                     return -1;
313                 }
314             }
315
316             fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
317                              FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
318                              FILE_FLAG_WRITE_THROUGH, NULL);
319             fflush(stdout);
320             if (fh == INVALID_HANDLE_VALUE) {
321                 gle = GetLastError();
322                 if (gle && ioctlDebug ) {
323                     char buf[4096];
324
325                     if ( FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
326                                         NULL,
327                                         gle,
328                                         MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),
329                                         buf,
330                                         4096,
331                                         (va_list *) NULL
332                                         ) )
333                     {
334                         fprintf(stderr,"pioctl CreateFile(%s) failed: 0x%8X\r\n\t[%s]\r\n",
335                                  tbuffer,gle,buf);
336                     }
337                 }
338                 return -1;
339             }
340         }
341     }
342
343     /* return fh and success code */
344     *handlep = fh;
345     return 0;
346 }
347
348 static long
349 Transceive(HANDLE handle, fs_ioctlRequest_t * reqp)
350 {
351     long rcount;
352     long ioCount;
353     DWORD gle;
354
355     rcount = reqp->mp - reqp->data;
356     if (rcount <= 0) {
357         if ( IoctlDebug() )
358             fprintf(stderr, "pioctl Transceive rcount <= 0: %d\r\n",rcount);
359         return EINVAL;          /* not supposed to happen */
360     }
361
362     if (!WriteFile(handle, reqp->data, rcount, &ioCount, NULL)) {
363         /* failed to write */
364         gle = GetLastError();
365
366         if ( IoctlDebug() )
367             fprintf(stderr, "pioctl Transceive WriteFile failed: 0x%X\r\n",gle);
368         return gle;
369     }
370
371     if (!ReadFile(handle, reqp->data, sizeof(reqp->data), &ioCount, NULL)) {
372         /* failed to read */
373         gle = GetLastError();
374
375         if ( IoctlDebug() )
376             fprintf(stderr, "pioctl Transceive ReadFile failed: 0x%X\r\n",gle);
377         return gle;
378     }
379
380     reqp->nbytes = ioCount;     /* set # of bytes available */
381     reqp->mp = reqp->data;      /* restart marshalling */
382
383     /* return success */
384     return 0;
385 }
386
387 static long
388 MarshallLong(fs_ioctlRequest_t * reqp, long val)
389 {
390     memcpy(reqp->mp, &val, 4);
391     reqp->mp += 4;
392     return 0;
393 }
394
395 static long
396 UnmarshallLong(fs_ioctlRequest_t * reqp, long *valp)
397 {
398     /* not enough data left */
399     if (reqp->nbytes < 4) {
400         if ( IoctlDebug() )
401             fprintf(stderr, "pioctl UnmarshallLong reqp->nbytes < 4: %d\r\n",
402                      reqp->nbytes);
403         return -1;
404     }
405
406     memcpy(valp, reqp->mp, 4);
407     reqp->mp += 4;
408     reqp->nbytes -= 4;
409     return 0;
410 }
411
412 /* includes marshalling NULL pointer as a null (0 length) string */
413 static long
414 MarshallString(fs_ioctlRequest_t * reqp, char *stringp)
415 {
416     int count;
417
418     if (stringp)
419         count = strlen(stringp) + 1;    /* space required including null */
420     else
421         count = 1;
422
423     /* watch for buffer overflow */
424     if ((reqp->mp - reqp->data) + count > sizeof(reqp->data)) {
425         if ( IoctlDebug() )
426             fprintf(stderr, "pioctl MarshallString buffer overflow\r\n");
427         return -1;
428     }
429
430     if (stringp)
431         memcpy(reqp->mp, stringp, count);
432     else
433         *(reqp->mp) = 0;
434     reqp->mp += count;
435     return 0;
436 }
437
438 /* take a path with a drive letter, possibly relative, and return a full path
439  * without the drive letter.  This is the full path relative to the working
440  * dir for that drive letter.  The input and output paths can be the same.
441  */
442 static long
443 fs_GetFullPath(char *pathp, char *outPathp, long outSize)
444 {
445     char tpath[1000];
446     char origPath[1000];
447     char *firstp;
448     long code;
449     int pathHasDrive;
450     int doSwitch;
451     char newPath[3];
452
453     if (pathp[0] != 0 && pathp[1] == ':') {
454         /* there's a drive letter there */
455         firstp = pathp + 2;
456         pathHasDrive = 1;
457     } else {
458         firstp = pathp;
459         pathHasDrive = 0;
460     }
461
462     if ( firstp[0] == '\\' && firstp[1] == '\\') {
463         /* UNC path - strip off the server and sharename */
464         int i, count;
465         for ( i=2,count=2; count < 4 && firstp[i]; i++ ) {
466             if ( firstp[i] == '\\' || firstp[i] == '/' ) {
467                 count++;
468             }
469         }
470         if ( firstp[i] == 0 ) {
471             strcpy(outPathp,"\\");
472         } else {
473             strcpy(outPathp,&firstp[--i]);
474         }
475         return 0;
476     } else if (firstp[0] == '\\' || firstp[0] == '/') {
477         /* already an absolute pathname, just copy it back */
478         strcpy(outPathp, firstp);
479         return 0;
480     }
481
482     GetCurrentDirectory(sizeof(origPath), origPath);
483
484     doSwitch = 0;
485     if (pathHasDrive && (*pathp & ~0x20) != (origPath[0] & ~0x20)) {
486         /* a drive has been specified and it isn't our current drive.
487          * to get path, switch to it first.  Must case-fold drive letters
488          * for user convenience.
489          */
490         doSwitch = 1;
491         newPath[0] = *pathp;
492         newPath[1] = ':';
493         newPath[2] = 0;
494         if (!SetCurrentDirectory(newPath)) {
495             code = GetLastError();
496
497             if ( IoctlDebug() )
498                 fprintf(stderr, "pioctl fs_GetFullPath SetCurrentDirectory(%s) failed: 0x%X\r\n",
499                          newPath, code);
500             return code;
501         }
502     }
503
504     /* now get the absolute path to the current wdir in this drive */
505     GetCurrentDirectory(sizeof(tpath), tpath);
506     if (tpath[1] == ':')
507         strcpy(outPathp, tpath + 2);    /* skip drive letter */
508     else if ( tpath[0] == '\\' && tpath[1] == '\\') {
509         /* UNC path - strip off the server and sharename */
510         int i, count;
511         for ( i=2,count=2; count < 4 && tpath[i]; i++ ) {
512             if ( tpath[i] == '\\' || tpath[i] == '/' ) {
513                 count++;
514             }
515         }
516         if ( tpath[i] == 0 ) {
517             strcpy(outPathp,"\\");
518         } else {
519             strcpy(outPathp,&tpath[--i]);
520         }
521     } else {
522         /* this should never happen */
523         strcpy(outPathp, tpath);
524     }
525
526     /* if there is a non-null name after the drive, append it */
527     if (*firstp != 0) {
528         int len = strlen(outPathp);
529         if (outPathp[len-1] != '\\' && outPathp[len-1] != '/') 
530             strcat(outPathp, "\\");
531         strcat(outPathp, firstp);
532     }
533
534     /* finally, if necessary, switch back to our home drive letter */
535     if (doSwitch) {
536         SetCurrentDirectory(origPath);
537     }
538
539     return 0;
540 }
541
542 long
543 pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow)
544 {
545     fs_ioctlRequest_t preq;
546     long code;
547     long temp;
548     char fullPath[1000];
549     HANDLE reqHandle;
550
551     code = GetIoctlHandle(pathp, &reqHandle);
552     if (code) {
553         if (pathp)
554             errno = EINVAL;
555         else
556             errno = ENODEV;
557         return code;
558     }
559
560     /* init the request structure */
561     InitFSRequest(&preq);
562
563     /* marshall the opcode, the path name and the input parameters */
564     MarshallLong(&preq, opcode);
565     /* when marshalling the path, remove the drive letter, since we already
566      * used the drive letter to find the AFS daemon; we don't need it any more.
567      * Eventually we'll expand relative path names here, too, since again, only
568      * we understand those.
569      */
570     if (pathp) {
571         code = fs_GetFullPath(pathp, fullPath, sizeof(fullPath));
572         if (code) {
573             CloseHandle(reqHandle);
574             errno = EINVAL;
575             return code;
576         }
577     } else {
578         strcpy(fullPath, "");
579     }
580
581     MarshallString(&preq, fullPath);
582     if (blobp->in_size) {
583         memcpy(preq.mp, blobp->in, blobp->in_size);
584         preq.mp += blobp->in_size;
585     }
586
587     /* now make the call */
588     code = Transceive(reqHandle, &preq);
589     if (code) {
590         CloseHandle(reqHandle);
591         return code;
592     }
593
594     /* now unmarshall the return value */
595     if (UnmarshallLong(&preq, &temp) != 0) {
596         CloseHandle(reqHandle);
597         return -1;
598     }
599
600     if (temp != 0) {
601         CloseHandle(reqHandle);
602         errno = CMtoUNIXerror(temp);
603         if ( IoctlDebug() )
604             fprintf(stderr, "pioctl temp != 0: 0x%X\r\n",temp);
605         return -1;
606     }
607
608     /* otherwise, unmarshall the output parameters */
609     if (blobp->out_size) {
610         temp = blobp->out_size;
611         if (preq.nbytes < temp)
612             temp = preq.nbytes;
613         memcpy(blobp->out, preq.mp, temp);
614         blobp->out_size = temp;
615     }
616
617     /* and return success */
618     CloseHandle(reqHandle);
619     return 0;
620 }