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