windows-misc-20040803
[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 long
102 GetIoctlHandle(char *fileNamep, HANDLE * handlep)
103 {
104     char *drivep;
105     char netbiosName[MAX_NB_NAME_LENGTH];
106     char tbuffer[256]="";
107     HANDLE fh;
108
109     if (fileNamep) {
110         drivep = strchr(fileNamep, ':');
111         if (drivep && (drivep - fileNamep) >= 1) {
112             tbuffer[0] = *(drivep - 1);
113             tbuffer[1] = ':';
114             strcpy(tbuffer + 2, SMB_IOCTL_FILENAME);
115         } else if (fileNamep[0] == fileNamep[1] && 
116                                fileNamep[0] == '\\')
117         {
118             int count = 0, i = 0;
119
120             while (count < 4 && fileNamep[i]) {
121                 tbuffer[i] = fileNamep[i];
122                 if ( tbuffer[i++] == '\\' )
123                     count++;
124             }
125             if (fileNamep[i] == 0)
126                 tbuffer[i++] = '\\';
127             tbuffer[i] = 0;
128             strcat(tbuffer, SMB_IOCTL_FILENAME);
129         } else {
130             char curdir[256]="";
131
132             GetCurrentDirectory(sizeof(curdir), curdir);
133             if ( curdir[1] == ':' ) {
134                 tbuffer[0] = curdir[0];
135                 tbuffer[1] = ':';
136                 strcpy(tbuffer + 2, SMB_IOCTL_FILENAME);
137             } else if (curdir[0] == curdir[1] &&
138                        curdir[0] == '\\') 
139             {
140                 int count = 0, i = 0;
141
142                 while (count < 4 && curdir[i]) {
143                     tbuffer[i] = curdir[i];
144                     if ( tbuffer[i++] == '\\' )
145                         count++;
146                 }
147                 if (tbuffer[i] == 0)
148                     tbuffer[i++] = '\\';
149                 tbuffer[i] = 0;
150                 strcat(tbuffer, SMB_IOCTL_FILENAME_NOSLASH);
151             }
152         }
153         }
154         if (!tbuffer[0]) {
155         /* No file name starting with drive colon specified, use UNC name */
156         lana_GetNetbiosName(netbiosName,LANA_NETBIOS_NAME_FULL);
157         sprintf(tbuffer,"\\\\%s\\all%s",netbiosName,SMB_IOCTL_FILENAME);
158     }
159
160     fflush(stdout);
161     /* now open the file */
162     fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
163                     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
164                     FILE_FLAG_WRITE_THROUGH, NULL);
165     fflush(stdout);
166         if (fh == INVALID_HANDLE_VALUE) {
167                 if (GetLastError() == ERROR_DOWNGRADE_DETECTED)
168                         fprintf(stderr, "Unable to open \"%s\": Authentication Downgrade Detected\n", tbuffer);
169                 return -1;
170         }
171     /* return fh and success code */
172     *handlep = fh;
173     return 0;
174 }
175
176 static long
177 Transceive(HANDLE handle, fs_ioctlRequest_t * reqp)
178 {
179     long rcount;
180     long ioCount;
181
182     rcount = reqp->mp - reqp->data;
183     if (rcount <= 0)
184         return EINVAL;          /* not supposed to happen */
185
186     if (!WriteFile(handle, reqp->data, rcount, &ioCount, NULL)) {
187         /* failed to write */
188         return GetLastError();
189     }
190
191     if (!ReadFile(handle, reqp->data, sizeof(reqp->data), &ioCount, NULL)) {
192         /* failed to read */
193         return GetLastError();
194     }
195
196     reqp->nbytes = ioCount;     /* set # of bytes available */
197     reqp->mp = reqp->data;      /* restart marshalling */
198
199     /* return success */
200     return 0;
201 }
202
203 static long
204 MarshallLong(fs_ioctlRequest_t * reqp, long val)
205 {
206     memcpy(reqp->mp, &val, 4);
207     reqp->mp += 4;
208     return 0;
209 }
210
211 static long
212 UnmarshallLong(fs_ioctlRequest_t * reqp, long *valp)
213 {
214     /* not enough data left */
215     if (reqp->nbytes < 4) {
216         return -1;
217     }
218
219     memcpy(valp, reqp->mp, 4);
220     reqp->mp += 4;
221     reqp->nbytes -= 4;
222     return 0;
223 }
224
225 /* includes marshalling NULL pointer as a null (0 length) string */
226 static long
227 MarshallString(fs_ioctlRequest_t * reqp, char *stringp)
228 {
229     int count;
230
231     if (stringp)
232         count = strlen(stringp) + 1;    /* space required including null */
233     else
234         count = 1;
235
236     /* watch for buffer overflow */
237     if ((reqp->mp - reqp->data) + count > sizeof(reqp->data))
238         return -1;
239
240     if (stringp)
241         memcpy(reqp->mp, stringp, count);
242     else
243         *(reqp->mp) = 0;
244     reqp->mp += count;
245     return 0;
246 }
247
248 /* take a path with a drive letter, possibly relative, and return a full path
249  * without the drive letter.  This is the full path relative to the working
250  * dir for that drive letter.  The input and output paths can be the same.
251  */
252 static long
253 fs_GetFullPath(char *pathp, char *outPathp, long outSize)
254 {
255     char tpath[1000];
256     char origPath[1000];
257     char *firstp;
258     long code;
259     int pathHasDrive;
260     int doSwitch;
261     char newPath[3];
262
263     if (pathp[0] != 0 && pathp[1] == ':') {
264         /* there's a drive letter there */
265         firstp = pathp + 2;
266         pathHasDrive = 1;
267     } else {
268         firstp = pathp;
269         pathHasDrive = 0;
270     }
271
272     if ( firstp[0] == '\\' && firstp[1] == '\\') {
273         /* UNC path - strip off the server and sharename */
274         int i, count;
275         for ( i=2,count=2; count < 4 && firstp[i]; i++ ) {
276             if ( firstp[i] == '\\' || firstp[i] == '/' ) {
277                 count++;
278             }
279         }
280         if ( firstp[i] == 0 ) {
281             strcpy(outPathp,"\\");
282         } else {
283             strcpy(outPathp,&firstp[--i]);
284         }
285         return 0;
286     } else if (firstp[0] == '\\' || firstp[0] == '/') {
287         /* already an absolute pathname, just copy it back */
288         strcpy(outPathp, firstp);
289         return 0;
290     }
291
292     GetCurrentDirectory(sizeof(origPath), origPath);
293
294     doSwitch = 0;
295     if (pathHasDrive && (*pathp & ~0x20) != (origPath[0] & ~0x20)) {
296         /* a drive has been specified and it isn't our current drive.
297          * to get path, switch to it first.  Must case-fold drive letters
298          * for user convenience.
299          */
300         doSwitch = 1;
301         newPath[0] = *pathp;
302         newPath[1] = ':';
303         newPath[2] = 0;
304         if (!SetCurrentDirectory(newPath)) {
305             code = GetLastError();
306             return code;
307         }
308     }
309
310     /* now get the absolute path to the current wdir in this drive */
311     GetCurrentDirectory(sizeof(tpath), tpath);
312         if (tpath[1] == ':')
313             strcpy(outPathp, tpath + 2);        /* skip drive letter */
314         else if ( tpath[0] == '\\' && tpath[1] == '\\') {
315         /* UNC path - strip off the server and sharename */
316         int i, count;
317         for ( i=2,count=2; count < 4 && tpath[i]; i++ ) {
318             if ( tpath[i] == '\\' || tpath[i] == '/' ) {
319                 count++;
320             }
321         }
322         if ( tpath[i] == 0 ) {
323             strcpy(outPathp,"\\");
324         } else {
325             strcpy(outPathp,&tpath[--i]);
326         }
327     } else {
328         /* this should never happen */
329         strcpy(outPathp, tpath);
330     }
331
332     /* if there is a non-null name after the drive, append it */
333     if (*firstp != 0) {
334                 int len = strlen(outPathp);
335                 if (outPathp[len-1] != '\\' && outPathp[len-1] != '/') 
336                         strcat(outPathp, "\\");
337                 strcat(outPathp, firstp);
338     }
339
340     /* finally, if necessary, switch back to our home drive letter */
341     if (doSwitch) {
342         SetCurrentDirectory(origPath);
343     }
344
345     return 0;
346 }
347
348 long
349 pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow)
350 {
351     fs_ioctlRequest_t preq;
352     long code;
353     long temp;
354     char fullPath[1000];
355     HANDLE reqHandle;
356
357     code = GetIoctlHandle(pathp, &reqHandle);
358     if (code) {
359         if (pathp)
360             errno = EINVAL;
361         else
362             errno = ENODEV;
363         return code;
364     }
365
366     /* init the request structure */
367     InitFSRequest(&preq);
368
369     /* marshall the opcode, the path name and the input parameters */
370     MarshallLong(&preq, opcode);
371     /* when marshalling the path, remove the drive letter, since we already
372      * used the drive letter to find the AFS daemon; we don't need it any more.
373      * Eventually we'll expand relative path names here, too, since again, only
374      * we understand those.
375      */
376     if (pathp) {
377         code = fs_GetFullPath(pathp, fullPath, sizeof(fullPath));
378         if (code) {
379             CloseHandle(reqHandle);
380             errno = EINVAL;
381             return code;
382         }
383     } else {
384         strcpy(fullPath, "");
385     }
386
387     MarshallString(&preq, fullPath);
388     if (blobp->in_size) {
389         memcpy(preq.mp, blobp->in, blobp->in_size);
390         preq.mp += blobp->in_size;
391     }
392
393     /* now make the call */
394     code = Transceive(reqHandle, &preq);
395     if (code) {
396         CloseHandle(reqHandle);
397         return code;
398     }
399
400     /* now unmarshall the return value */
401     UnmarshallLong(&preq, &temp);
402     if (temp != 0) {
403         CloseHandle(reqHandle);
404         errno = CMtoUNIXerror(temp);
405         return -1;
406     }
407
408     /* otherwise, unmarshall the output parameters */
409     if (blobp->out_size) {
410         temp = blobp->out_size;
411         if (preq.nbytes < temp)
412             temp = preq.nbytes;
413         memcpy(blobp->out, preq.mp, temp);
414         blobp->out_size = temp;
415     }
416
417     /* and return success */
418     CloseHandle(reqHandle);
419     return 0;
420 }