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