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