reindent-20030715
[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 \r
39 #include <smb.h>
40 #include <pioctl_nt.h>
41 static char AFSConfigKeyName[] =
42     "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters";
43
44 #define FS_IOCTLREQUEST_MAXSIZE 8192
45 /* big structure for representing and storing an IOCTL request */
46 typedef struct fs_ioctlRequest {
47     char *mp;                   /* marshalling/unmarshalling ptr */
48     long nbytes;                /* bytes received (when unmarshalling) */
49     char data[FS_IOCTLREQUEST_MAXSIZE]; /* data we're marshalling */
50 } fs_ioctlRequest_t;
51
52 static int
53 CMtoUNIXerror(int cm_code)
54 {
55     switch (cm_code) {
56     case CM_ERROR_TIMEDOUT:
57         return ETIMEDOUT;
58     case CM_ERROR_NOACCESS:
59         return EACCES;
60     case CM_ERROR_NOSUCHFILE:
61         return ENOENT;
62     case CM_ERROR_INVAL:
63         return EINVAL;
64     case CM_ERROR_BADFD:
65         return EBADF;
66     case CM_ERROR_EXISTS:
67         return EEXIST;
68     case CM_ERROR_CROSSDEVLINK:
69         return EXDEV;
70     case CM_ERROR_NOTDIR:
71         return ENOTDIR;
72     case CM_ERROR_ISDIR:
73         return EISDIR;
74     case CM_ERROR_READONLY:
75         return EROFS;
76     case CM_ERROR_WOULDBLOCK:
77         return EWOULDBLOCK;
78     case CM_ERROR_NOSUCHCELL:
79         return ESRCH;           /* hack */
80     case CM_ERROR_NOSUCHVOLUME:
81         return EPIPE;           /* hack */
82     case CM_ERROR_NOMORETOKENS:
83         return EDOM;            /* hack */
84     case CM_ERROR_TOOMANYBUFS:
85         return EFBIG;           /* hack */
86     default:
87         return ENOTTY;
88     }
89 }
90
91 static void
92 InitFSRequest(fs_ioctlRequest_t * rp)
93 {
94     rp->mp = rp->data;
95     rp->nbytes = 0;
96 }
97
98 static long
99 GetIoctlHandle(char *fileNamep, HANDLE * handlep)
100 {
101     char *drivep;
102     char hostName[256];
103     char tbuffer[100];
104     char *ctemp;
105     HANDLE fh;
106     HKEY parmKey;
107     DWORD dummyLen;
108     long code;
109
110     if (fileNamep) {
111         drivep = strchr(fileNamep, ':');
112         if (drivep && (drivep - fileNamep) >= 1) {
113             tbuffer[0] = *(drivep - 1);
114             tbuffer[1] = ':';
115             strcpy(tbuffer + 2, SMB_IOCTL_FILENAME);
116         } else
117             strcpy(tbuffer, SMB_IOCTL_FILENAME);
118     } else {
119         /* No file name specified, use UNC name */
120         /* First look for gateway host in Registry */
121         code =
122             RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSConfigKeyName, 0,
123                          KEY_QUERY_VALUE, &parmKey);
124         if (code != ERROR_SUCCESS)
125             goto nogateway;
126         dummyLen = sizeof(hostName);
127         code =
128             RegQueryValueEx(parmKey, "Gateway", NULL, NULL, hostName,
129                             &dummyLen);
130         RegCloseKey(parmKey);
131         if (code == ERROR_SUCCESS)
132             goto havehost;
133       nogateway:
134         /* No gateway name in registry; use ourself */
135 #ifndef AFS_WIN95_ENV
136         gethostname(hostName, sizeof(hostName));
137 #else
138         {
139             int hostsize;
140             /* DJGPP version of gethostname gets the NetBIOS
141              * name of the machine, so that is what we are using for
142              * the AFS server name instead of the DNS name. */
143             hostsize = sizeof(hostName);
144             GetComputerName(hostName, &hostsize);
145         }
146 #endif /* AFS_WIN95_ENV */
147
148       havehost:
149         ctemp = strchr(hostName, '.');  /* turn ntafs.* into ntafs */
150         if (ctemp)
151             *ctemp = 0;
152         hostName[11] = 0;
153
154         _strupr(hostName);
155         sprintf(tbuffer, "\\\\%s-AFS\\all%s", hostName, SMB_IOCTL_FILENAME);
156     }
157
158     fflush(stdout);
159     /* now open the file */
160     fh = CreateFile(tbuffer, GENERIC_READ | GENERIC_WRITE,
161                     FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
162                     FILE_FLAG_WRITE_THROUGH, NULL);
163     fflush(stdout);
164     if (fh == INVALID_HANDLE_VALUE)
165         return -1;
166
167     /* return fh and success code */
168     *handlep = fh;
169     return 0;
170 }
171
172 static long
173 Transceive(HANDLE handle, fs_ioctlRequest_t * reqp)
174 {
175     long rcount;
176     long ioCount;
177
178     rcount = reqp->mp - reqp->data;
179     if (rcount <= 0)
180         return EINVAL;          /* not supposed to happen */
181
182     if (!WriteFile(handle, reqp->data, rcount, &ioCount, NULL)) {
183         /* failed to write */
184         return GetLastError();
185     }
186
187     if (!ReadFile(handle, reqp->data, sizeof(reqp->data), &ioCount, NULL)) {
188         /* failed to read */
189         return GetLastError();
190     }
191
192     reqp->nbytes = ioCount;     /* set # of bytes available */
193     reqp->mp = reqp->data;      /* restart marshalling */
194
195     /* return success */
196     return 0;
197 }
198
199 static long
200 MarshallLong(fs_ioctlRequest_t * reqp, long val)
201 {
202     memcpy(reqp->mp, &val, 4);
203     reqp->mp += 4;
204     return 0;
205 }
206
207 static long
208 UnmarshallLong(fs_ioctlRequest_t * reqp, long *valp)
209 {
210     /* not enough data left */
211     if (reqp->nbytes < 4) {
212         return -1;
213     }
214
215     memcpy(valp, reqp->mp, 4);
216     reqp->mp += 4;
217     reqp->nbytes -= 4;
218     return 0;
219 }
220
221 /* includes marshalling NULL pointer as a null (0 length) string */
222 static long
223 MarshallString(fs_ioctlRequest_t * reqp, char *stringp)
224 {
225     int count;
226
227     if (stringp)
228         count = strlen(stringp) + 1;    /* space required including null */
229     else
230         count = 1;
231
232     /* watch for buffer overflow */
233     if ((reqp->mp - reqp->data) + count > sizeof(reqp->data))
234         return -1;
235
236     if (stringp)
237         memcpy(reqp->mp, stringp, count);
238     else
239         *(reqp->mp) = 0;
240     reqp->mp += count;
241     return 0;
242 }
243
244 /* take a path with a drive letter, possibly relative, and return a full path
245  * without the drive letter.  This is the full path relative to the working
246  * dir for that drive letter.  The input and output paths can be the same.
247  */
248 static long
249 fs_GetFullPath(char *pathp, char *outPathp, long outSize)
250 {
251     char tpath[1000];
252     char origPath[1000];
253     char *firstp;
254     long code;
255     int pathHasDrive;
256     int doSwitch;
257     char newPath[3];
258
259     if (pathp[0] != 0 && pathp[1] == ':') {
260         /* there's a drive letter there */
261         firstp = pathp + 2;
262         pathHasDrive = 1;
263     } else {
264         firstp = pathp;
265         pathHasDrive = 0;
266     }
267
268     if (*firstp == '\\' || *firstp == '/') {
269         /* already an absolute pathname, just copy it back */
270         strcpy(outPathp, firstp);
271         return 0;
272     }
273
274     GetCurrentDirectory(sizeof(origPath), origPath);
275
276     doSwitch = 0;
277     if (pathHasDrive && (*pathp & ~0x20) != (origPath[0] & ~0x20)) {
278         /* a drive has been specified and it isn't our current drive.
279          * to get path, switch to it first.  Must case-fold drive letters
280          * for user convenience.
281          */
282         doSwitch = 1;
283         newPath[0] = *pathp;
284         newPath[1] = ':';
285         newPath[2] = 0;
286         if (!SetCurrentDirectory(newPath)) {
287             code = GetLastError();
288             return code;
289         }
290     }
291
292     /* now get the absolute path to the current wdir in this drive */
293     GetCurrentDirectory(sizeof(tpath), tpath);
294     strcpy(outPathp, tpath + 2);        /* skip drive letter */
295     /* if there is a non-null name after the drive, append it */
296     if (*firstp != 0) {
297         strcat(outPathp, "\\");
298         strcat(outPathp, firstp);
299     }
300
301     /* finally, if necessary, switch back to our home drive letter */
302     if (doSwitch) {
303         SetCurrentDirectory(origPath);
304     }
305
306     return 0;
307 }
308
309 long
310 pioctl(char *pathp, long opcode, struct ViceIoctl *blobp, int follow)
311 {
312     fs_ioctlRequest_t preq;
313     long code;
314     long temp;
315     char fullPath[1000];
316     HANDLE reqHandle;
317
318     code = GetIoctlHandle(pathp, &reqHandle);
319     if (code) {
320         if (pathp)
321             errno = EINVAL;
322         else
323             errno = ENODEV;
324         return code;
325     }
326
327     /* init the request structure */
328     InitFSRequest(&preq);
329
330     /* marshall the opcode, the path name and the input parameters */
331     MarshallLong(&preq, opcode);
332     /* when marshalling the path, remove the drive letter, since we already
333      * used the drive letter to find the AFS daemon; we don't need it any more.
334      * Eventually we'll expand relative path names here, too, since again, only
335      * we understand those.
336      */
337     if (pathp) {
338         code = fs_GetFullPath(pathp, fullPath, sizeof(fullPath));
339         if (code) {
340             CloseHandle(reqHandle);
341             errno = EINVAL;
342             return code;
343         }
344     } else {
345         strcpy(fullPath, "");
346     }
347
348     MarshallString(&preq, fullPath);
349     if (blobp->in_size) {
350         memcpy(preq.mp, blobp->in, blobp->in_size);
351         preq.mp += blobp->in_size;
352     }
353
354     /* now make the call */
355     code = Transceive(reqHandle, &preq);
356     if (code) {
357         CloseHandle(reqHandle);
358         return code;
359     }
360
361     /* now unmarshall the return value */
362     UnmarshallLong(&preq, &temp);
363     if (temp != 0) {
364         CloseHandle(reqHandle);
365         errno = CMtoUNIXerror(temp);
366         return -1;
367     }
368
369     /* otherwise, unmarshall the output parameters */
370     if (blobp->out_size) {
371         temp = blobp->out_size;
372         if (preq.nbytes < temp)
373             temp = preq.nbytes;
374         memcpy(blobp->out, preq.mp, temp);
375         blobp->out_size = temp;
376     }
377
378     /* and return success */
379     CloseHandle(reqHandle);
380     return 0;
381 }