rx-protect-queue-during-debug-20040601
[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[100];
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
116             strcpy(tbuffer, SMB_IOCTL_FILENAME);
117     } else {
118         /* No file name specified, use UNC name */
119         lana_GetNetbiosName(netbiosName,LANA_NETBIOS_NAME_FULL);
120         sprintf(tbuffer,"\\\\%s\\all%s",netbiosName,SMB_IOCTL_FILENAME);
121     }
122
123     fflush(stdout);
124     /* now open the file */
125     fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
126                     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
127                     FILE_FLAG_WRITE_THROUGH, NULL);
128     fflush(stdout);
129     if (fh == INVALID_HANDLE_VALUE)
130         return -1;
131
132     /* return fh and success code */
133     *handlep = fh;
134     return 0;
135 }
136
137 static long
138 Transceive(HANDLE handle, fs_ioctlRequest_t * reqp)
139 {
140     long rcount;
141     long ioCount;
142
143     rcount = reqp->mp - reqp->data;
144     if (rcount <= 0)
145         return EINVAL;          /* not supposed to happen */
146
147     if (!WriteFile(handle, reqp->data, rcount, &ioCount, NULL)) {
148         /* failed to write */
149         return GetLastError();
150     }
151
152     if (!ReadFile(handle, reqp->data, sizeof(reqp->data), &ioCount, NULL)) {
153         /* failed to read */
154         return GetLastError();
155     }
156
157     reqp->nbytes = ioCount;     /* set # of bytes available */
158     reqp->mp = reqp->data;      /* restart marshalling */
159
160     /* return success */
161     return 0;
162 }
163
164 static long
165 MarshallLong(fs_ioctlRequest_t * reqp, long val)
166 {
167     memcpy(reqp->mp, &val, 4);
168     reqp->mp += 4;
169     return 0;
170 }
171
172 static long
173 UnmarshallLong(fs_ioctlRequest_t * reqp, long *valp)
174 {
175     /* not enough data left */
176     if (reqp->nbytes < 4) {
177         return -1;
178     }
179
180     memcpy(valp, reqp->mp, 4);
181     reqp->mp += 4;
182     reqp->nbytes -= 4;
183     return 0;
184 }
185
186 /* includes marshalling NULL pointer as a null (0 length) string */
187 static long
188 MarshallString(fs_ioctlRequest_t * reqp, char *stringp)
189 {
190     int count;
191
192     if (stringp)
193         count = strlen(stringp) + 1;    /* space required including null */
194     else
195         count = 1;
196
197     /* watch for buffer overflow */
198     if ((reqp->mp - reqp->data) + count > sizeof(reqp->data))
199         return -1;
200
201     if (stringp)
202         memcpy(reqp->mp, stringp, count);
203     else
204         *(reqp->mp) = 0;
205     reqp->mp += count;
206     return 0;
207 }
208
209 /* take a path with a drive letter, possibly relative, and return a full path
210  * without the drive letter.  This is the full path relative to the working
211  * dir for that drive letter.  The input and output paths can be the same.
212  */
213 static long
214 fs_GetFullPath(char *pathp, char *outPathp, long outSize)
215 {
216     char tpath[1000];
217     char origPath[1000];
218     char *firstp;
219     long code;
220     int pathHasDrive;
221     int doSwitch;
222     char newPath[3];
223
224     if (pathp[0] != 0 && pathp[1] == ':') {
225         /* there's a drive letter there */
226         firstp = pathp + 2;
227         pathHasDrive = 1;
228     } else {
229         firstp = pathp;
230         pathHasDrive = 0;
231     }
232
233     if (*firstp == '\\' || *firstp == '/') {
234         /* already an absolute pathname, just copy it back */
235         strcpy(outPathp, firstp);
236         return 0;
237     }
238
239     GetCurrentDirectory(sizeof(origPath), origPath);
240
241     doSwitch = 0;
242     if (pathHasDrive && (*pathp & ~0x20) != (origPath[0] & ~0x20)) {
243         /* a drive has been specified and it isn't our current drive.
244          * to get path, switch to it first.  Must case-fold drive letters
245          * for user convenience.
246          */
247         doSwitch = 1;
248         newPath[0] = *pathp;
249         newPath[1] = ':';
250         newPath[2] = 0;
251         if (!SetCurrentDirectory(newPath)) {
252             code = GetLastError();
253             return code;
254         }
255     }
256
257     /* now get the absolute path to the current wdir in this drive */
258     GetCurrentDirectory(sizeof(tpath), tpath);
259     strcpy(outPathp, tpath + 2);        /* skip drive letter */
260     /* if there is a non-null name after the drive, append it */
261     if (*firstp != 0) {
262         strcat(outPathp, "\\");
263         strcat(outPathp, firstp);
264     }
265
266     /* finally, if necessary, switch back to our home drive letter */
267     if (doSwitch) {
268         SetCurrentDirectory(origPath);
269     }
270
271     return 0;
272 }
273
274 long
275 pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow)
276 {
277     fs_ioctlRequest_t preq;
278     long code;
279     long temp;
280     char fullPath[1000];
281     HANDLE reqHandle;
282
283     code = GetIoctlHandle(pathp, &reqHandle);
284     if (code) {
285         if (pathp)
286             errno = EINVAL;
287         else
288             errno = ENODEV;
289         return code;
290     }
291
292     /* init the request structure */
293     InitFSRequest(&preq);
294
295     /* marshall the opcode, the path name and the input parameters */
296     MarshallLong(&preq, opcode);
297     /* when marshalling the path, remove the drive letter, since we already
298      * used the drive letter to find the AFS daemon; we don't need it any more.
299      * Eventually we'll expand relative path names here, too, since again, only
300      * we understand those.
301      */
302     if (pathp) {
303         code = fs_GetFullPath(pathp, fullPath, sizeof(fullPath));
304         if (code) {
305             CloseHandle(reqHandle);
306             errno = EINVAL;
307             return code;
308         }
309     } else {
310         strcpy(fullPath, "");
311     }
312
313     MarshallString(&preq, fullPath);
314     if (blobp->in_size) {
315         memcpy(preq.mp, blobp->in, blobp->in_size);
316         preq.mp += blobp->in_size;
317     }
318
319     /* now make the call */
320     code = Transceive(reqHandle, &preq);
321     if (code) {
322         CloseHandle(reqHandle);
323         return code;
324     }
325
326     /* now unmarshall the return value */
327     UnmarshallLong(&preq, &temp);
328     if (temp != 0) {
329         CloseHandle(reqHandle);
330         errno = CMtoUNIXerror(temp);
331         return -1;
332     }
333
334     /* otherwise, unmarshall the output parameters */
335     if (blobp->out_size) {
336         temp = blobp->out_size;
337         if (preq.nbytes < temp)
338             temp = preq.nbytes;
339         memcpy(blobp->out, preq.mp, temp);
340         blobp->out_size = temp;
341     }
342
343     /* and return success */
344     CloseHandle(reqHandle);
345     return 0;
346 }