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