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