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